Compare commits

..

1 Commits

Author SHA1 Message Date
Ameer J
ff73c135b3 shader_recompiler: derivate -> derivative 2023-11-17 22:59:21 -05:00
89 changed files with 1140 additions and 2572 deletions

View File

@@ -1,67 +0,0 @@
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
param (
[string]$ClangPath
)
Set-PSDebug -Trace 1
# Set good encoding for input
$OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding
# Set good encoding for clang-output
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
$src_trailing=Get-ChildItem -Path src -Exclude "*.png","*.jpg","*.jar" -Recurse | Select-String -Pattern '\s$' -List | Select-Object -Property Path, LineNumber, Line
$files_trailing=Get-ChildItem -Path "*.txt", "*.md", "Doxyfile", ".gitignore", ".gitmodules", ".ci*", "dist/*.desktop", "dist/*.svg", "dist/*.xml" | Select-String -Pattern '\s$' -List | Select-Object -Property Path, LineNumber, Line
if ($src_trailing -or $files_trailing) {
Write-Output $src_trailing
Write-Output $files_trailing
Write-Output "`nTrailing whitespace found, aborting"
exit 1
}
# Set default clang-format
if ($ClangPath) {
if (Test-Path $ClangPath) {
$CLANG_FORMAT=$ClangPath
} else {
Write-Error 'Invalid path given as ClangPath'
exit 1
}
} else {
if (-not $CLANG_FORMAT) {
$CLANG_FORMAT='build/externals/clang-format-15.exe'
}
}
Invoke-expression "$CLANG_FORMAT --version"
if ("$TRAVIS_EVENT_TYPE" -eq 'pull_request') {
# Get list of every file modified in this pull request
$files_to_lint="$(git diff --name-only --diff-filter=ACMRTUXB $TRAVIS_COMMIT_RANGE | grep '^src/[^.]*[.]\(cpp\|h\)$')"
} else {
# Check everything for branch pushes
$files_to_lint=Get-ChildItem -Path src -Include '*.cpp','*.h' -Recurse
}
# Turn off tracing for this because it's too verbose
Set-PSDebug -Trace 0
foreach ($f in $files_to_lint) {
$orig=Get-Content $f | %{$i = 1} { New-Object psobject -prop @{LineNum=$i;Text=$_}; $i++}
$formated=(Invoke-expression "$CLANG_FORMAT -style=file '$f'") | %{$i = 1} { New-Object psobject -prop @{LineNum=$i;Text=$_}; $i++}
$diff=Compare-Object $orig $formated -Property Text -PassThru
if ($diff) {
Write-Output "!!! $f not compliant to coding style, here is the fix:"
Write-Output $diff | Format-Table
$fail=1
}
}
Set-PSDebug -Trace 0
if ($fail -eq 1) {
exit 1
}

View File

@@ -3,7 +3,7 @@
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
if grep -nrI '\s$' src *.txt *.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop \
if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop \
dist/*.svg dist/*.xml; then
echo Trailing whitespace found, aborting
exit 1

View File

@@ -9,7 +9,7 @@ chmod a+x ./.ci/scripts/linux/docker.sh
sudo chown -R 1027 ./
# The environment variables listed below:
# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION
# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION
# are requested in src/common/CMakeLists.txt and appear to be provided somewhere in Azure DevOps
docker run -e AZURECIREPO -e TITLEBARFORMATIDLE -e TITLEBARFORMATRUNNING -e DISPLAYVERSION -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh "$1"

View File

@@ -6,8 +6,3 @@ file_filter = <lang>.ts
source_file = en.ts
source_lang = en
type = QT
[o:yuzu-emulator:p:yuzu:r:yuzu-android]
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
source_file = ../../src/android/app/src/main/res/values/strings.xml
type = ANDROID

View File

@@ -73,7 +73,7 @@ abstract class SettingsItem(
R.string.frame_limit_slider,
R.string.frame_limit_slider_description,
1,
400,
200,
"%"
)
)

View File

@@ -21,8 +21,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.ui.main.MainActivity
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class InstallableFragment : Fragment() {
private var _binding: FragmentInstallablesBinding? = null
@@ -80,15 +78,7 @@ class InstallableFragment : Fragment() {
R.string.manage_save_data,
R.string.import_export_saves_description,
install = { mainActivity.importSaves.launch(arrayOf("application/zip")) },
export = {
mainActivity.exportSaves.launch(
"yuzu saves - ${
LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
)
}.zip"
)
}
export = { mainActivity.exportSave() }
)
} else {
Installable(

View File

@@ -18,8 +18,8 @@ class Game(
val version: String = "",
val isHomebrew: Boolean = false
) : Parcelable {
val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${path}_LastPlayed"
val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${programId}_LastPlayed"
override fun equals(other: Any?): Boolean {
if (other !is Game) {

View File

@@ -6,6 +6,7 @@ package org.yuzu.yuzu_emu.ui.main
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.DocumentsContract
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import android.view.WindowManager
@@ -19,6 +20,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
@@ -39,6 +41,7 @@ import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@@ -50,6 +53,9 @@ import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.*
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.FileOutputStream
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
@@ -67,6 +73,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
// Get first subfolder in saves folder (should be the user folder)
val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
private var lastZipCreated: File? = null
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
@@ -649,31 +656,75 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
/**
* Zips the save files located in the given folder path and creates a new zip file with the current date and time.
* @return true if the zip file is successfully created, false otherwise.
*/
private fun zipSave(): Boolean {
try {
val tempFolder = File(getPublicFilesDir().canonicalPath, "temp")
tempFolder.mkdirs()
val saveFolder = File(savesFolderRoot)
val outputZipFile = File(
tempFolder,
"yuzu saves - ${
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
}.zip"
)
outputZipFile.createNewFile()
val result = FileUtil.zipFromInternalStorage(
saveFolder,
savesFolderRoot,
BufferedOutputStream(FileOutputStream(outputZipFile))
)
if (result == TaskState.Failed) {
return false
}
lastZipCreated = outputZipFile
} catch (e: Exception) {
return false
}
return true
}
/**
* Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
*/
val exportSaves = registerForActivityResult(
ActivityResultContracts.CreateDocument("application/zip")
) { result ->
if (result == null) {
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
this,
R.string.save_files_exporting,
false
) {
val zipResult = FileUtil.zipFromInternalStorage(
File(savesFolderRoot),
savesFolderRoot,
BufferedOutputStream(contentResolver.openOutputStream(result))
)
return@newInstance when (zipResult) {
TaskState.Completed -> getString(R.string.export_success)
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
fun exportSave() {
CoroutineScope(Dispatchers.IO).launch {
val wasZipCreated = zipSave()
val lastZipFile = lastZipCreated
if (!wasZipCreated || lastZipFile == null) {
withContext(Dispatchers.Main) {
Toast.makeText(
this@MainActivity,
getString(R.string.export_save_failed),
Toast.LENGTH_LONG
).show()
}
return@launch
}
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
withContext(Dispatchers.Main) {
val file = DocumentFile.fromSingleUri(
this@MainActivity,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
)
)!!
val intent = Intent(Intent.ACTION_SEND)
.setDataAndType(file.uri, "application/zip")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, file.uri)
startForResultExportSave.launch(
Intent.createChooser(
intent,
getString(R.string.share_save_file)
)
)
}
}
}
private val startForResultExportSave =

View File

@@ -127,7 +127,6 @@
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingVertical="4dp"
app:checkedChip="@id/chip_recently_played"
app:chipSpacingHorizontal="12dp"
app:singleLine="true"
app:singleSelection="true">

View File

@@ -91,7 +91,6 @@
<string name="manage_save_data">Manage save data</string>
<string name="manage_save_data_description">Save data found. Please select an option below.</string>
<string name="import_export_saves_description">Import or export save files</string>
<string name="save_files_exporting">Exporting save files…</string>
<string name="save_file_imported_success">Imported successfully</string>
<string name="save_file_invalid_zip_structure">Invalid save directory structure</string>
<string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string>
@@ -257,7 +256,6 @@
<string name="cancelling">Cancelling</string>
<string name="install">Install</string>
<string name="delete">Delete</string>
<string name="export_success">Exported successfully</string>
<!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string>

View File

@@ -12,7 +12,7 @@ bool IsValidChannelCount(u32 channel_count) {
}
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
return total_stream_count > 0 && stereo_stream_count > 0 &&
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
}
} // namespace

View File

@@ -148,7 +148,7 @@ Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
OpusPacketHeader header{ReverseHeader(*header_p)};
LOG_TRACE(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
header.size, input_data.size_bytes(), in_data.size_bytes());
R_UNLESS(in_data.size_bytes() >= header.size &&

View File

@@ -523,8 +523,6 @@ add_library(core STATIC
hle/service/hid/hid.h
hle/service/hid/hid_debug_server.cpp
hle/service/hid/hid_debug_server.h
hle/service/hid/hid_firmware_settings.cpp
hle/service/hid/hid_firmware_settings.h
hle/service/hid/hid_server.cpp
hle/service/hid/hid_server.h
hle/service/hid/hid_system_server.cpp
@@ -725,7 +723,6 @@ add_library(core STATIC
hle/service/nvnflinger/producer_listener.h
hle/service/nvnflinger/status.h
hle/service/nvnflinger/ui/fence.h
hle/service/nvnflinger/ui/graphic_buffer.cpp
hle/service/nvnflinger/ui/graphic_buffer.h
hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp

View File

@@ -218,13 +218,6 @@ enum class NpadIdType : u32 {
Invalid = 0xFFFFFFFF,
};
enum class NpadInterfaceType : u8 {
Bluetooth = 1,
Rail = 2,
Usb = 3,
Embedded = 4,
};
// This is nn::hid::NpadStyleIndex
enum class NpadStyleIndex : u8 {
None = 0,

View File

@@ -13,7 +13,6 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
@@ -22,7 +21,6 @@
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/am/applets/applet_controller.h"
#include "core/hle/service/am/applets/applet_mii_edit_types.h"
#include "core/hle/service/am/applets/applet_profile_select.h"
#include "core/hle/service/am/applets/applet_software_keyboard_types.h"
@@ -37,7 +35,6 @@
#include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
@@ -76,7 +73,7 @@ IWindowController::IWindowController(Core::System& system_)
static const FunctionInfo functions[] = {
{0, nullptr, "CreateWindow"},
{1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
{2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"},
{2, nullptr, "GetAppletResourceUserIdOfCallerApplet"},
{10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
{11, nullptr, "ReleaseForegroundRights"},
{12, nullptr, "RejectToChangeIntoBackground"},
@@ -100,16 +97,6 @@ void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) {
rb.Push<u64>(process_id);
}
void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) {
const u64 process_id = 0;
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(process_id);
}
void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
@@ -1578,7 +1565,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
{6, nullptr, "GetPopInteractiveInDataEvent"},
{10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
{11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
{12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
{12, nullptr, "GetMainAppletIdentityInfo"},
{13, nullptr, "CanUseApplicationCore"},
{14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
{15, nullptr, "GetMainAppletApplicationControlProperty"},
@@ -1622,9 +1609,6 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
case Applets::AppletId::SoftwareKeyboard:
PushInShowSoftwareKeyboard();
break;
case Applets::AppletId::Controller:
PushInShowController();
break;
default:
break;
}
@@ -1682,33 +1666,13 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
rb.PushRaw(applet_info);
}
void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
struct AppletIdentityInfo {
Applets::AppletId applet_id;
INSERT_PADDING_BYTES(0x4);
u64 application_id;
};
static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
LOG_WARNING(Service_AM, "(STUBBED) called");
const AppletIdentityInfo applet_info{
.applet_id = Applets::AppletId::QLaunch,
.application_id = 0x0100000000001000ull,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(applet_info);
}
void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
struct AppletIdentityInfo {
Applets::AppletId applet_id;
INSERT_PADDING_BYTES(0x4);
u64 application_id;
};
static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
LOG_WARNING(Service_AM, "(STUBBED) called");
const AppletIdentityInfo applet_info{
@@ -1773,55 +1737,6 @@ void ILibraryAppletSelfAccessor::PushInShowAlbum() {
queue_data.emplace_back(std::move(settings_data));
}
void ILibraryAppletSelfAccessor::PushInShowController() {
const Applets::CommonArguments common_args = {
.arguments_version = Applets::CommonArgumentVersion::Version3,
.size = Applets::CommonArgumentSize::Version3,
.library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8),
.theme_color = Applets::ThemeColor::BasicBlack,
.play_startup_sound = true,
.system_tick = system.CoreTiming().GetClockTicks(),
};
Applets::ControllerSupportArgNew user_args = {
.header = {.player_count_min = 1,
.player_count_max = 4,
.enable_take_over_connection = true,
.enable_left_justify = false,
.enable_permit_joy_dual = true,
.enable_single_mode = false,
.enable_identification_color = false},
.identification_colors = {},
.enable_explain_text = false,
.explain_text = {},
};
Applets::ControllerSupportArgPrivate private_args = {
.arg_private_size = sizeof(Applets::ControllerSupportArgPrivate),
.arg_size = sizeof(Applets::ControllerSupportArgNew),
.is_home_menu = true,
.flag_1 = true,
.mode = Applets::ControllerSupportMode::ShowControllerSupport,
.caller = Applets::ControllerSupportCaller::
Application, // switchbrew: Always zero except with
// ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
// which sets this to the input param
.style_set = Core::HID::NpadStyleSet::None,
.joy_hold_type = 0,
};
std::vector<u8> common_args_data(sizeof(common_args));
std::vector<u8> private_args_data(sizeof(private_args));
std::vector<u8> user_args_data(sizeof(user_args));
std::memcpy(common_args_data.data(), &common_args, sizeof(common_args));
std::memcpy(private_args_data.data(), &private_args, sizeof(private_args));
std::memcpy(user_args_data.data(), &user_args, sizeof(user_args));
queue_data.emplace_back(std::move(common_args_data));
queue_data.emplace_back(std::move(private_args_data));
queue_data.emplace_back(std::move(user_args_data));
}
void ILibraryAppletSelfAccessor::PushInShowCabinetData() {
const Applets::CommonArguments arguments{
.arguments_version = Applets::CommonArgumentVersion::Version3,

View File

@@ -87,7 +87,6 @@ public:
private:
void GetAppletResourceUserId(HLERequestContext& ctx);
void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
void AcquireForegroundRights(HLERequestContext& ctx);
};
@@ -346,7 +345,6 @@ private:
void PopInData(HLERequestContext& ctx);
void PushOutData(HLERequestContext& ctx);
void GetLibraryAppletInfo(HLERequestContext& ctx);
void GetMainAppletIdentityInfo(HLERequestContext& ctx);
void ExitProcessAndReturn(HLERequestContext& ctx);
void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
void GetDesirableKeyboardLayout(HLERequestContext& ctx);
@@ -357,7 +355,6 @@ private:
void PushInShowCabinetData();
void PushInShowMiiEditData();
void PushInShowSoftwareKeyboard();
void PushInShowController();
std::deque<std::vector<u8>> queue_data;
};

View File

@@ -56,7 +56,7 @@ enum class ControllerSupportResult : u32 {
struct ControllerSupportArgPrivate {
u32 arg_private_size{};
u32 arg_size{};
bool is_home_menu{};
bool flag_0{};
bool flag_1{};
ControllerSupportMode mode{};
ControllerSupportCaller caller{};

View File

@@ -127,7 +127,7 @@ public:
private:
void GetCore(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "called");
LOG_DEBUG(Service_BTM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -263,13 +263,13 @@ public:
explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"},
{1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"},
{0, nullptr, "StartGamepadPairing"},
{1, nullptr, "CancelGamepadPairing"},
{2, nullptr, "ClearGamepadPairingDatabase"},
{3, nullptr, "GetPairedGamepadCount"},
{4, nullptr, "EnableRadio"},
{5, nullptr, "DisableRadio"},
{6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"},
{6, nullptr, "GetRadioOnOff"},
{7, nullptr, "AcquireRadioEvent"},
{8, nullptr, "AcquireGamepadPairingEvent"},
{9, nullptr, "IsGamepadPairingStarted"},
@@ -280,58 +280,18 @@ public:
{14, nullptr, "AcquireAudioDeviceConnectionEvent"},
{15, nullptr, "ConnectAudioDevice"},
{16, nullptr, "IsConnectingAudioDevice"},
{17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"},
{17, nullptr, "GetConnectedAudioDevices"},
{18, nullptr, "DisconnectAudioDevice"},
{19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
{20, nullptr, "GetPairedAudioDevices"},
{21, nullptr, "RemoveAudioDevicePairing"},
{22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"},
{23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"}
{22, nullptr, "RequestAudioDeviceConnectionRejection"},
{23, nullptr, "CancelAudioDeviceConnectionRejection"}
};
// clang-format on
RegisterHandlers(functions);
}
private:
void IsRadioEnabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(true);
}
void StartGamepadPairing(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void CancelGamepadPairing(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetConnectedAudioDevices(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0);
}
void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
class BTM_SYS final : public ServiceFramework<BTM_SYS> {
@@ -348,7 +308,7 @@ public:
private:
void GetCore(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "called");
LOG_DEBUG(Service_BTM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);

View File

@@ -32,7 +32,7 @@ public:
{10200, nullptr, "SendFriendRequestForApplication"},
{10211, nullptr, "AddFacedFriendRequestForApplication"},
{10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
{10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"},
{10420, nullptr, "IsBlockedUserListCacheAvailable"},
{10421, nullptr, "EnsureBlockedUserListAvailable"},
{10500, nullptr, "GetProfileList"},
{10600, nullptr, "DeclareOpenOnlinePlaySession"},
@@ -206,17 +206,6 @@ private:
rb.Push(true);
}
void CheckBlockedUserListAvailability(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto uuid{rp.PopRaw<Common::UUID>()};
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;

View File

@@ -8,17 +8,12 @@ namespace Service::HID {
ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
ControllerBase::~ControllerBase() = default;
Result ControllerBase::Activate() {
void ControllerBase::ActivateController() {
if (is_activated) {
return ResultSuccess;
return;
}
is_activated = true;
OnInit();
return ResultSuccess;
}
Result ControllerBase::Activate(u64 aruid) {
return Activate();
}
void ControllerBase::DeactivateController() {

View File

@@ -4,7 +4,6 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/result.h"
namespace Core::Timing {
class CoreTiming;
@@ -32,8 +31,7 @@ public:
// When the controller is requesting a motion update for the shared memory
virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
Result Activate();
Result Activate(u64 aruid);
void ActivateController();
void DeactivateController();

View File

@@ -344,7 +344,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
Common::Input::PollingMode::Active);
}
SignalStyleSetChangedEvent(npad_id);
WriteEmptyEntry(controller.shared_memory);
hid_core.SetLastActiveController(npad_id);
@@ -1727,19 +1726,4 @@ const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
}
}
Controller_NPad::AppletDetailedUiType Controller_NPad::GetAppletDetailedUiType(
Core::HID::NpadIdType npad_id) {
auto controller = GetControllerFromNpadIdType(npad_id);
auto shared_memory = controller.shared_memory;
Service::HID::Controller_NPad::AppletFooterUiType applet_footer_type =
shared_memory->applet_footer_type;
Controller_NPad::AppletDetailedUiType detailed_ui_type{
.ui_variant = 0,
.footer = applet_footer_type,
};
return detailed_ui_type;
}
} // namespace Service::HID

View File

@@ -78,46 +78,6 @@ public:
MaxActivationMode = 3,
};
// This is nn::hid::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
INSERT_PADDING_BYTES(0x4);
};
// This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
None = 0,
HandheldNone = 1,
HandheldJoyConLeftOnly = 2,
HandheldJoyConRightOnly = 3,
HandheldJoyConLeftJoyConRight = 4,
JoyDual = 5,
JoyDualLeftOnly = 6,
JoyDualRightOnly = 7,
JoyLeftHorizontal = 8,
JoyLeftVertical = 9,
JoyRightHorizontal = 10,
JoyRightVertical = 11,
SwitchProController = 12,
CompatibleProController = 13,
CompatibleJoyCon = 14,
LarkHvc1 = 15,
LarkHvc2 = 16,
LarkNesLeft = 17,
LarkNesRight = 18,
Lucia = 19,
Verification = 20,
Lagon = 21,
};
using AppletFooterUiVariant = u8;
// This is "nn::hid::system::AppletDetailedUiType".
struct AppletDetailedUiType {
AppletFooterUiVariant ui_variant;
INSERT_PADDING_BYTES(0x2);
AppletFooterUiType footer;
};
static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
// This is nn::hid::NpadCommunicationMode
enum class NpadCommunicationMode : u64 {
Mode_5ms = 0,
@@ -126,13 +86,6 @@ public:
Default = 3,
};
enum class NpadRevision : u32 {
Revision0 = 0,
Revision1 = 1,
Revision2 = 2,
Revision3 = 3,
};
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
Core::HID::NpadStyleTag GetSupportedStyleSet() const;
@@ -243,7 +196,6 @@ public:
static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
static Result VerifyValidSixAxisSensorHandle(
const Core::HID::SixAxisSensorHandle& device_handle);
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
private:
static constexpr std::size_t NPAD_COUNT = 10;
@@ -401,6 +353,37 @@ private:
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
"NfcXcdDeviceHandleStateImpl is an invalid size");
// This is nn::hid::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
INSERT_PADDING_BYTES(0x4);
};
// This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
None = 0,
HandheldNone = 1,
HandheldJoyConLeftOnly = 2,
HandheldJoyConRightOnly = 3,
HandheldJoyConLeftJoyConRight = 4,
JoyDual = 5,
JoyDualLeftOnly = 6,
JoyDualRightOnly = 7,
JoyLeftHorizontal = 8,
JoyLeftVertical = 9,
JoyRightHorizontal = 10,
JoyRightVertical = 11,
SwitchProController = 12,
CompatibleProController = 13,
CompatibleJoyCon = 14,
LarkHvc1 = 15,
LarkHvc2 = 16,
LarkNesLeft = 17,
LarkNesRight = 18,
Lucia = 19,
Verification = 20,
Lagon = 21,
};
// This is nn::hid::NpadLarkType
enum class NpadLarkType : u32 {
Invalid,

View File

@@ -44,7 +44,7 @@ Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
Activate();
ActivateController();
return ResultSuccess;
}

View File

@@ -3,7 +3,6 @@
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/hid_debug_server.h"
#include "core/hle/service/hid/hid_firmware_settings.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/hid/hid_system_server.h"
#include "core/hle/service/hid/hidbus.h"
@@ -17,11 +16,9 @@ namespace Service::HID {
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system);
std::shared_ptr<HidFirmwareSettings> firmware_settings =
std::make_shared<HidFirmwareSettings>();
server_manager->RegisterNamedService(
"hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings));
server_manager->RegisterNamedService("hid",
std::make_shared<IHidServer>(system, resouce_manager));
server_manager->RegisterNamedService(
"hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager));
server_manager->RegisterNamedService(

View File

@@ -1,99 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid/hid_firmware_settings.h"
namespace Service::HID {
HidFirmwareSettings::HidFirmwareSettings() {
LoadSettings(true);
}
void HidFirmwareSettings::Reload() {
LoadSettings(true);
}
void HidFirmwareSettings::LoadSettings(bool reload_config) {
if (is_initalized && !reload_config) {
return;
}
// TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values
is_debug_pad_enabled = true;
is_device_managed = true;
is_touch_i2c_managed = is_device_managed;
is_future_devices_emulated = false;
is_mcu_hardware_error_emulated = false;
is_rail_enabled = true;
is_firmware_update_failure_emulated = false;
is_firmware_update_failure = {};
is_ble_disabled = false;
is_dscale_disabled = false;
is_handheld_forced = true;
features_per_id_disabled = {};
is_touch_firmware_auto_update_disabled = false;
is_initalized = true;
}
bool HidFirmwareSettings::IsDebugPadEnabled() {
LoadSettings(false);
return is_debug_pad_enabled;
}
bool HidFirmwareSettings::IsDeviceManaged() {
LoadSettings(false);
return is_device_managed;
}
bool HidFirmwareSettings::IsEmulateFutureDevice() {
LoadSettings(false);
return is_future_devices_emulated;
}
bool HidFirmwareSettings::IsTouchI2cManaged() {
LoadSettings(false);
return is_touch_i2c_managed;
}
bool HidFirmwareSettings::IsHandheldForced() {
LoadSettings(false);
return is_handheld_forced;
}
bool HidFirmwareSettings::IsRailEnabled() {
LoadSettings(false);
return is_rail_enabled;
}
bool HidFirmwareSettings::IsHardwareErrorEmulated() {
LoadSettings(false);
return is_mcu_hardware_error_emulated;
}
bool HidFirmwareSettings::IsBleDisabled() {
LoadSettings(false);
return is_ble_disabled;
}
bool HidFirmwareSettings::IsDscaleDisabled() {
LoadSettings(false);
return is_dscale_disabled;
}
bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() {
LoadSettings(false);
return is_touch_firmware_auto_update_disabled;
}
HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() {
LoadSettings(false);
return is_firmware_update_failure;
}
HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() {
LoadSettings(false);
return features_per_id_disabled;
}
} // namespace Service::HID

View File

@@ -1,54 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
namespace Service::HID {
/// Loads firmware config from nn::settings::fwdbg
class HidFirmwareSettings {
public:
using FirmwareSetting = std::array<u8, 4>;
using FeaturesPerId = std::array<bool, 0xA8>;
HidFirmwareSettings();
void Reload();
void LoadSettings(bool reload_config);
bool IsDebugPadEnabled();
bool IsDeviceManaged();
bool IsEmulateFutureDevice();
bool IsTouchI2cManaged();
bool IsHandheldForced();
bool IsRailEnabled();
bool IsHardwareErrorEmulated();
bool IsBleDisabled();
bool IsDscaleDisabled();
bool IsTouchAutoUpdateDisabled();
FirmwareSetting GetFirmwareUpdateFailure();
FeaturesPerId FeaturesDisabledPerId();
private:
bool is_initalized{};
// Debug settings
bool is_debug_pad_enabled{};
bool is_device_managed{};
bool is_touch_i2c_managed{};
bool is_future_devices_emulated{};
bool is_mcu_hardware_error_emulated{};
bool is_rail_enabled{};
bool is_firmware_update_failure_emulated{};
bool is_ble_disabled{};
bool is_dscale_disabled{};
bool is_handheld_forced{};
bool is_touch_firmware_auto_update_disabled{};
FirmwareSetting is_firmware_update_failure{};
FeaturesPerId features_per_id_disabled{};
};
} // namespace Service::HID

View File

@@ -10,7 +10,6 @@
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid_firmware_settings.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/hid/resource_manager.h"
#include "core/hle/service/ipc_helpers.h"
@@ -65,9 +64,8 @@ private:
std::shared_ptr<ResourceManager> resource_manager;
};
IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
std::shared_ptr<HidFirmwareSettings> settings)
: ServiceFramework{system_, "hid"}, resource_manager{resource}, firmware_settings{settings} {
IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
: ServiceFramework{system_, "hid"}, resource_manager{resource} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IHidServer::CreateAppletResource, "CreateAppletResource"},
@@ -232,87 +230,48 @@ void IHidServer::ActivateDebugPad(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
GetResourceManager()->ActivateController(HidController::DebugPad);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
Result result = ResultSuccess;
auto& debug_pad =
GetResourceManager()->GetController<Controller_DebugPad>(HidController::DebugPad);
if (!firmware_settings->IsDeviceManaged()) {
result = debug_pad.Activate();
}
if (result.IsSuccess()) {
result = debug_pad.Activate(applet_resource_user_id);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
GetResourceManager()->ActivateController(HidController::Touchscreen);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
Result result = ResultSuccess;
auto& touch_screen =
GetResourceManager()->GetController<Controller_Touchscreen>(HidController::Touchscreen);
if (!firmware_settings->IsDeviceManaged()) {
result = touch_screen.Activate();
}
if (result.IsSuccess()) {
result = touch_screen.Activate(applet_resource_user_id);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IHidServer::ActivateMouse(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
GetResourceManager()->ActivateController(HidController::Mouse);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
Result result = ResultSuccess;
auto& mouse = GetResourceManager()->GetController<Controller_Mouse>(HidController::Mouse);
if (!firmware_settings->IsDeviceManaged()) {
result = mouse.Activate();
}
if (result.IsSuccess()) {
result = mouse.Activate(applet_resource_user_id);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IHidServer::ActivateKeyboard(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
GetResourceManager()->ActivateController(HidController::Keyboard);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
Result result = ResultSuccess;
auto& keyboard =
GetResourceManager()->GetController<Controller_Keyboard>(HidController::Keyboard);
if (!firmware_settings->IsDeviceManaged()) {
result = keyboard.Activate();
}
if (result.IsSuccess()) {
result = keyboard.Activate(applet_resource_user_id);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IHidServer::SendKeyboardLockKeyEvent(HLERequestContext& ctx) {
@@ -939,7 +898,7 @@ void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx)
void IHidServer::ActivateGesture(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 basic_gesture_id;
u32 unknown;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -947,23 +906,13 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
parameters.basic_gesture_id, parameters.applet_resource_user_id);
GetResourceManager()->ActivateController(HidController::Gesture);
Result result = ResultSuccess;
auto& gesture = GetResourceManager()->GetController<Controller_Gesture>(HidController::Gesture);
if (!firmware_settings->IsDeviceManaged()) {
result = gesture.Activate();
}
if (result.IsSuccess()) {
// TODO: Use gesture id here
result = gesture.Activate(parameters.applet_resource_user_id);
}
LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
parameters.unknown, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
@@ -1020,24 +969,21 @@ void IHidServer::ActivateNpad(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
GetResourceManager()->ActivateController(HidController::NPad);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
auto& npad = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad);
// TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0);
const Result result = npad.Activate(applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IHidServer::DeactivateNpad(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
GetResourceManager()->DeactivateController(HidController::NPad);
// This function does nothing since 10.0.0+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1107,9 +1053,10 @@ void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) {
}
void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
// Should have no effect with how our npad sets up the data
IPC::RequestParser rp{ctx};
struct Parameters {
Controller_NPad::NpadRevision revision;
s32 revision;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -1117,16 +1064,13 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
GetResourceManager()->ActivateController(HidController::NPad);
LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
parameters.applet_resource_user_id);
auto& npad = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad);
// TODO: npad->SetRevision(applet_resource_user_id, revision);
const auto result = npad.Activate(parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) {
@@ -1222,8 +1166,8 @@ void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
controller.SetNpadMode(new_npad_id, parameters.npad_id, {},
Controller_NPad::NpadJoyAssignmentMode::Dual);
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id); // Spams a lot when controller applet is open
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1563,7 +1507,7 @@ void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IActiveVibrationDeviceList>(system, GetResourceManager());
rb.PushIpcInterface<IActiveVibrationDeviceList>(system, resource_manager);
}
void IHidServer::PermitVibration(HLERequestContext& ctx) {
@@ -1774,22 +1718,12 @@ void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
GetResourceManager()->ActivateController(HidController::ConsoleSixAxisSensor);
Result result = ResultSuccess;
auto console_sixaxis = GetResourceManager()->GetController<Controller_ConsoleSixAxis>(
HidController::ConsoleSixAxisSensor);
if (!firmware_settings->IsDeviceManaged()) {
result = console_sixaxis.Activate();
}
if (result.IsSuccess()) {
result = console_sixaxis.Activate(applet_resource_user_id);
}
LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
rb.Push(ResultSuccess);
}
void IHidServer::StartConsoleSixAxisSensor(HLERequestContext& ctx) {
@@ -1836,19 +1770,9 @@ void IHidServer::ActivateSevenSixAxisSensor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
GetResourceManager()->ActivateController(HidController::ConsoleSixAxisSensor);
Result result = ResultSuccess;
auto console_sixaxis = GetResourceManager()->GetController<Controller_ConsoleSixAxis>(
HidController::ConsoleSixAxisSensor);
if (!firmware_settings->IsDeviceManaged()) {
result = console_sixaxis.Activate();
}
if (result.IsSuccess()) {
console_sixaxis.Activate(applet_resource_user_id);
}
LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1913,7 +1837,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
// Activate console six axis controller
GetResourceManager()
->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
.Activate();
.ActivateController();
GetResourceManager()
->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)

View File

@@ -11,12 +11,10 @@ class System;
namespace Service::HID {
class ResourceManager;
class HidFirmwareSettings;
class IHidServer final : public ServiceFramework<IHidServer> {
public:
explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
std::shared_ptr<HidFirmwareSettings> settings);
explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
~IHidServer() override;
std::shared_ptr<ResourceManager> GetResourceManager();
@@ -143,7 +141,6 @@ private:
void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
std::shared_ptr<ResourceManager> resource_manager;
std::shared_ptr<HidFirmwareSettings> firmware_settings;
};
} // namespace Service::HID

View File

@@ -36,24 +36,24 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
{301, nullptr, "ActivateNpadSystem"},
{303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
{304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"},
{305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"},
{304, nullptr, "EnableAssigningSingleOnSlSrPress"},
{305, nullptr, "DisableAssigningSingleOnSlSrPress"},
{306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"},
{307, nullptr, "GetNpadSystemExtStyle"},
{308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"},
{309, &IHidSystemServer::GetNpadFullKeyGripColor, "GetNpadFullKeyGripColor"},
{310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"},
{308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
{309, nullptr, "GetNpadFullKeyGripColor"},
{310, nullptr, "GetMaskedSupportedNpadStyleSet"},
{311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
{312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"},
{312, nullptr, "SetSupportedNpadStyleSetAll"},
{313, nullptr, "GetNpadCaptureButtonAssignment"},
{314, nullptr, "GetAppletFooterUiType"},
{315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"},
{316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"},
{317, &IHidSystemServer::GetNpadLeftRightInterfaceType, "GetNpadLeftRightInterfaceType"},
{318, &IHidSystemServer::HasBattery, "HasBattery"},
{319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"},
{315, nullptr, "GetAppletDetailedUiType"},
{316, nullptr, "GetNpadInterfaceType"},
{317, nullptr, "GetNpadLeftRightInterfaceType"},
{318, nullptr, "HasBattery"},
{319, nullptr, "HasLeftRightBattery"},
{321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
{322, &IHidSystemServer::GetIrSensorState, "GetIrSensorState"},
{322, nullptr, "GetIrSensorState"},
{323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
{324, nullptr, "GetUniquePadButtonSet"},
{325, nullptr, "GetUniquePadColor"},
@@ -85,15 +85,15 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{541, nullptr, "GetPlayReportControllerUsages"},
{542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
{543, nullptr, "GetRegisteredDevicesOld"},
{544, &IHidSystemServer::AcquireConnectionTriggerTimeoutEvent, "AcquireConnectionTriggerTimeoutEvent"},
{544, nullptr, "AcquireConnectionTriggerTimeoutEvent"},
{545, nullptr, "SendConnectionTrigger"},
{546, &IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport, "AcquireDeviceRegisteredEventForControllerSupport"},
{546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
{547, nullptr, "GetAllowedBluetoothLinksCount"},
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
{548, nullptr, "GetRegisteredDevices"},
{549, nullptr, "GetConnectableRegisteredDevices"},
{700, nullptr, "ActivateUniquePad"},
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
{702, nullptr, "AcquireUniquePadConnectionEventHandle"},
{703, nullptr, "GetUniquePadIds"},
{751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
{800, nullptr, "ListSixAxisSensorHandles"},
{801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
@@ -123,10 +123,10 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
{851, nullptr, "EnableUsbFullKeyController"},
{852, nullptr, "IsUsbConnected"},
{870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"},
{870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
{900, nullptr, "ActivateInputDetector"},
{901, nullptr, "NotifyInputDetector"},
{1000, &IHidSystemServer::InitializeFirmwareUpdate, "InitializeFirmwareUpdate"},
{1000, nullptr, "InitializeFirmwareUpdate"},
{1001, nullptr, "GetFirmwareVersion"},
{1002, nullptr, "GetAvailableFirmwareVersion"},
{1003, nullptr, "IsFirmwareUpdateAvailable"},
@@ -149,7 +149,6 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
{1133, nullptr, "StartUsbFirmwareUpdate"},
{1134, nullptr, "GetUsbFirmwareUpdateState"},
{1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
{1150, nullptr, "SetTouchScreenMagnification"},
{1151, nullptr, "GetTouchScreenFirmwareVersion"},
{1152, nullptr, "SetTouchScreenDefaultConfiguration"},
@@ -221,20 +220,11 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
RegisterHandlers(functions);
joy_detach_event = service_context.CreateEvent("IHidSystemServer::JoyDetachEvent");
acquire_device_registered_event =
service_context.CreateEvent("IHidSystemServer::AcquireDeviceRegisteredEvent");
acquire_connection_trigger_timeout_event =
service_context.CreateEvent("IHidSystemServer::AcquireConnectionTriggerTimeoutEvent");
unique_pad_connection_event =
service_context.CreateEvent("IHidSystemServer::AcquireUniquePadConnectionEventHandle");
joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
}
IHidSystemServer::~IHidSystemServer() {
service_context.CloseEvent(joy_detach_event);
service_context.CloseEvent(acquire_device_registered_event);
service_context.CloseEvent(acquire_connection_trigger_timeout_event);
service_context.CloseEvent(unique_pad_connection_event);
};
void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
@@ -248,241 +238,29 @@ void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
void IHidSystemServer::EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running
LOG_DEBUG(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(system.HIDCore().GetLastActiveController());
}
void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "called");
GetResourceManager()
->GetController<Controller_NPad>(HidController::NPad)
.ApplyNpadSystemCommonPolicy();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
Core::HID::NpadColor left_color{};
Core::HID::NpadColor right_color{};
// TODO: Get colors from Npad
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw(left_color);
rb.PushRaw(right_color);
}
void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
LOG_INFO(Service_HID, "(STUBBED) called");
Core::HID::NpadStyleSet supported_styleset =
GetResourceManager()
->GetController<Controller_NPad>(HidController::NPad)
.GetSupportedStyleSet()
.raw;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(supported_styleset);
}
void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
LOG_INFO(Service_HID, "(STUBBED) called");
Core::HID::NpadStyleSet supported_styleset =
GetResourceManager()
->GetController<Controller_NPad>(HidController::NPad)
.GetSupportedStyleSet()
.raw;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(supported_styleset);
}
void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
const Service::HID::Controller_NPad::AppletDetailedUiType detailed_ui_type =
GetResourceManager()
->GetController<Controller_NPad>(HidController::NPad)
.GetAppletDetailedUiType(npad_id_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(detailed_ui_type);
}
void IHidSystemServer::GetNpadInterfaceType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
}
void IHidSystemServer::GetNpadLeftRightInterfaceType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth);
}
void IHidSystemServer::HasBattery(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(false);
}
void IHidSystemServer::HasLeftRightBattery(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
struct LeftRightBattery {
bool left;
bool right;
};
LeftRightBattery left_right_battery{
.left = false,
.right = false,
};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(left_right_battery);
}
void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
const std::vector<Core::HID::UniquePadId> unique_pads{};
if (!unique_pads.empty()) {
ctx.WriteBuffer(unique_pads);
}
ctx.WriteBuffer(unique_pads);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(unique_pads.size()));
}
void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
}
void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
LOG_INFO(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
}
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
struct RegisterData {
std::array<u8, 0x68> data;
};
static_assert(sizeof(RegisterData) == 0x68, "RegisterData is an invalid size");
std::vector<RegisterData> registered_devices{};
if (!registered_devices.empty()) {
ctx.WriteBuffer(registered_devices);
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(registered_devices.size());
}
void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
rb.Push(ResultSuccess);
}
void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(0);
}
void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
@@ -501,31 +279,6 @@ void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
rb.Push(is_enabled);
}
void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) {
const bool button_pressed = false;
LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}",
button_pressed); // Spams a lot when controller applet is open
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(button_pressed);
}
void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");

View File

@@ -24,38 +24,15 @@ public:
private:
void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
void GetLastActiveNpad(HLERequestContext& ctx);
void ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx);
void GetNpadFullKeyGripColor(HLERequestContext& ctx);
void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx);
void SetSupportedNpadStyleSetAll(HLERequestContext& ctx);
void GetAppletDetailedUiType(HLERequestContext& ctx);
void GetNpadInterfaceType(HLERequestContext& ctx);
void GetNpadLeftRightInterfaceType(HLERequestContext& ctx);
void HasBattery(HLERequestContext& ctx);
void HasLeftRightBattery(HLERequestContext& ctx);
void GetUniquePadsFromNpad(HLERequestContext& ctx);
void GetIrSensorState(HLERequestContext& ctx);
void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
void GetRegisteredDevices(HLERequestContext& ctx);
void AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx);
void GetUniquePadIds(HLERequestContext& ctx);
void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx);
void InitializeFirmwareUpdate(HLERequestContext& ctx);
void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
std::shared_ptr<ResourceManager> GetResourceManager();
Kernel::KEvent* acquire_connection_trigger_timeout_event;
Kernel::KEvent* acquire_device_registered_event;
Kernel::KEvent* joy_detach_event;
Kernel::KEvent* unique_pad_connection_event;
KernelHelpers::ServiceContext service_context;
std::shared_ptr<ResourceManager> resource_manager;
};

View File

@@ -59,8 +59,8 @@ void ResourceManager::Initialize() {
MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory);
// Homebrew doesn't try to activate some controllers, so we activate them by default
GetController<Controller_NPad>(HidController::NPad).Activate();
GetController<Controller_Touchscreen>(HidController::Touchscreen).Activate();
GetController<Controller_NPad>(HidController::NPad).ActivateController();
GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00);
GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00);
@@ -73,6 +73,14 @@ void ResourceManager::Initialize() {
is_initialized = true;
}
void ResourceManager::ActivateController(HidController controller) {
controllers[static_cast<size_t>(controller)]->ActivateController();
}
void ResourceManager::DeactivateController(HidController controller) {
controllers[static_cast<size_t>(controller)]->DeactivateController();
}
void ResourceManager::UpdateControllers(std::uintptr_t user_data,
std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();

View File

@@ -55,6 +55,8 @@ public:
}
void Initialize();
void ActivateController(HidController controller);
void DeactivateController(HidController controller);
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);

View File

@@ -115,20 +115,12 @@ public:
{400, nullptr, "InitializeSystem"},
{401, nullptr, "FinalizeSystem"},
{402, nullptr, "SetOperationMode"},
{403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"},
{403, nullptr, "InitializeSystem2"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void InitializeSystem2(HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
class IUserLocalCommunicationService final

View File

@@ -15,7 +15,7 @@
namespace Service::android {
class GraphicBuffer;
struct GraphicBuffer;
class BufferItem final {
public:

View File

@@ -5,6 +5,7 @@
// 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/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
@@ -13,8 +14,9 @@
namespace Service::android {
BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
: core{std::move(core_)}, slots{core->slots} {}
BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
Service::Nvidia::NvCore::NvMap& nvmap_)
: core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
BufferQueueConsumer::~BufferQueueConsumer() = default;
@@ -134,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(), true);
listener = core->connected_producer_listener;
LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot);
@@ -171,25 +175,6 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
return Status::NoError;
}
Status BufferQueueConsumer::Disconnect() {
LOG_DEBUG(Service_Nvnflinger, "called");
std::scoped_lock lock{core->mutex};
if (core->consumer_listener == nullptr) {
LOG_ERROR(Service_Nvnflinger, "no consumer is connected");
return Status::BadValue;
}
core->is_abandoned = true;
core->consumer_listener = nullptr;
core->queue.clear();
core->FreeAllBuffersLocked();
core->SignalDequeueCondition();
return Status::NoError;
}
Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
if (out_slot_mask == nullptr) {
LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr");

View File

@@ -13,6 +13,10 @@
#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
#include "core/hle/service/nvnflinger/status.h"
namespace Service::Nvidia::NvCore {
class NvMap;
} // namespace Service::Nvidia::NvCore
namespace Service::android {
class BufferItem;
@@ -21,18 +25,19 @@ class IConsumerListener;
class BufferQueueConsumer final {
public:
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
Service::Nvidia::NvCore::NvMap& nvmap_);
~BufferQueueConsumer();
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
Status Disconnect();
Status GetReleasedBuffers(u64* out_slot_mask);
private:
std::shared_ptr<BufferQueueCore> core;
BufferQueueDefs::SlotsType& slots;
Service::Nvidia::NvCore::NvMap& nvmap;
};
} // namespace Service::android

View File

@@ -14,12 +14,24 @@ BufferQueueCore::BufferQueueCore() = default;
BufferQueueCore::~BufferQueueCore() = default;
void BufferQueueCore::NotifyShutdown() {
std::scoped_lock lock{mutex};
is_shutting_down = true;
SignalDequeueCondition();
}
void BufferQueueCore::SignalDequeueCondition() {
dequeue_possible.store(true);
dequeue_condition.notify_all();
}
bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
if (is_shutting_down) {
return false;
}
dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
dequeue_possible.store(false);

View File

@@ -34,6 +34,8 @@ public:
BufferQueueCore();
~BufferQueueCore();
void NotifyShutdown();
private:
void SignalDequeueCondition();
bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
@@ -72,6 +74,7 @@ private:
u32 transform_hint{};
bool is_allocating{};
mutable std::condition_variable_any is_allocating_condition;
bool is_shutting_down{};
};
} // namespace Service::android

View File

@@ -13,6 +13,7 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
#include "core/hle/service/nvnflinger/consumer_listener.h"
@@ -532,6 +533,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(), true);
sticky_transform = sticky_transform_;
if (core->queue.empty()) {
@@ -741,13 +744,19 @@ 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:
case NativeWindowApi::Media:
case NativeWindowApi::Camera:
if (core->connected_api == api) {
core->queue.clear();
core->FreeAllBuffersLocked();
core->connected_producer_listener = nullptr;
core->connected_api = NativeWindowApi::NoConnectedApi;
@@ -776,7 +785,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
}
Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
const std::shared_ptr<NvGraphicBuffer>& buffer) {
const std::shared_ptr<GraphicBuffer>& buffer) {
LOG_DEBUG(Service_Nvnflinger, "slot {}", slot);
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
@@ -787,7 +796,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
slots[slot] = {};
slots[slot].fence = Fence::NoFence();
slots[slot].graphic_buffer = std::make_shared<GraphicBuffer>(nvmap, buffer);
slots[slot].graphic_buffer = buffer;
slots[slot].frame_number = 0;
// Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
@@ -830,7 +839,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
}
case TransactionId::SetPreallocatedBuffer: {
const auto slot = parcel_in.Read<s32>();
const auto buffer = parcel_in.ReadObject<NvGraphicBuffer>();
const auto buffer = parcel_in.ReadObject<GraphicBuffer>();
status = SetPreallocatedBuffer(slot, buffer);
break;
@@ -858,7 +867,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
status = RequestBuffer(slot, &buf);
parcel_out.WriteFlattenedObject<NvGraphicBuffer>(buf.get());
parcel_out.WriteFlattenedObject(buf);
break;
}
case TransactionId::QueueBuffer: {

View File

@@ -38,7 +38,6 @@ namespace Service::android {
class BufferQueueCore;
class IProducerListener;
struct NvGraphicBuffer;
class BufferQueueProducer final : public IBinder {
public:
@@ -66,7 +65,7 @@ public:
bool producer_controlled_by_app, QueueBufferOutput* output);
Status Disconnect(NativeWindowApi api);
Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<NvGraphicBuffer>& buffer);
Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
private:
BufferQueueProducer(const BufferQueueProducer&) = delete;

View File

@@ -13,7 +13,7 @@
namespace Service::android {
class GraphicBuffer;
struct GraphicBuffer;
enum class BufferState : u32 {
Free = 0,

View File

@@ -27,26 +27,6 @@ void ConsumerBase::Connect(bool controlled_by_app) {
consumer->Connect(shared_from_this(), controlled_by_app);
}
void ConsumerBase::Abandon() {
LOG_DEBUG(Service_Nvnflinger, "called");
std::scoped_lock lock{mutex};
if (!is_abandoned) {
this->AbandonLocked();
is_abandoned = true;
}
}
void ConsumerBase::AbandonLocked() {
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
this->FreeBufferLocked(i);
}
// disconnect from the BufferQueue
consumer->Disconnect();
consumer = nullptr;
}
void ConsumerBase::FreeBufferLocked(s32 slot_index) {
LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index);

View File

@@ -24,7 +24,6 @@ class BufferQueueConsumer;
class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
public:
void Connect(bool controlled_by_app);
void Abandon();
protected:
explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
@@ -35,7 +34,6 @@ protected:
void OnBuffersReleased() override;
void OnSidebandStreamChanged() override;
void AbandonLocked();
void FreeBufferLocked(s32 slot_index);
Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer);

View File

@@ -166,7 +166,7 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
}();
void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
auto buffer = std::make_shared<android::NvGraphicBuffer>();
auto buffer = std::make_shared<android::GraphicBuffer>();
buffer->width = SharedBufferWidth;
buffer->height = SharedBufferHeight;
buffer->stride = SharedBufferBlockLinearStride;

View File

@@ -47,10 +47,7 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) {
vsync_signal.Wait();
const auto lock_guard = Lock();
if (!is_abandoned) {
Compose();
}
Compose();
}
}
@@ -101,6 +98,7 @@ Nvnflinger::~Nvnflinger() {
}
ShutdownLayers();
vsync_thread = {};
if (nvdrv) {
nvdrv->Close(disp_fd);
@@ -108,20 +106,12 @@ Nvnflinger::~Nvnflinger() {
}
void Nvnflinger::ShutdownLayers() {
// Abandon consumers.
{
const auto lock_guard = Lock();
for (auto& display : displays) {
for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
display.GetLayer(layer).GetConsumer().Abandon();
}
const auto lock_guard = Lock();
for (auto& display : displays) {
for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
display.GetLayer(layer).Core().NotifyShutdown();
}
is_abandoned = true;
}
// Join the vsync thread, if it exists.
vsync_thread = {};
}
void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {

View File

@@ -140,8 +140,6 @@ private:
s32 swap_interval = 1;
bool is_abandoned = false;
/// Event that handles screen composition.
std::shared_ptr<Core::Timing::EventType> multi_composition_event;
std::shared_ptr<Core::Timing::EventType> single_composition_event;

View File

@@ -19,7 +19,7 @@ enum class Status : s32 {
Busy = -16,
NoInit = -19,
BadValue = -22,
InvalidOperation = -38,
InvalidOperation = -37,
BufferNeedsReallocation = 1,
ReleaseAllBuffers = 2,
};

View File

@@ -1,34 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
namespace Service::android {
static NvGraphicBuffer GetBuffer(std::shared_ptr<NvGraphicBuffer>& buffer) {
if (buffer) {
return *buffer;
} else {
return {};
}
}
GraphicBuffer::GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
: NvGraphicBuffer(width_, height_, format_, usage_), m_nvmap(nullptr) {}
GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
std::shared_ptr<NvGraphicBuffer> buffer)
: NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) {
if (this->BufferId() > 0) {
m_nvmap->DuplicateHandle(this->BufferId(), true);
}
}
GraphicBuffer::~GraphicBuffer() {
if (m_nvmap != nullptr && this->BufferId() > 0) {
m_nvmap->FreeHandle(this->BufferId(), true);
}
}
} // namespace Service::android

View File

@@ -6,22 +6,16 @@
#pragma once
#include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/nvnflinger/pixel_format.h"
namespace Service::Nvidia::NvCore {
class NvMap;
} // namespace Service::Nvidia::NvCore
namespace Service::android {
struct NvGraphicBuffer {
constexpr NvGraphicBuffer() = default;
struct GraphicBuffer final {
constexpr GraphicBuffer() = default;
constexpr NvGraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
: width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
usage{static_cast<s32>(usage_)} {}
@@ -99,17 +93,6 @@ struct NvGraphicBuffer {
u32 offset{};
INSERT_PADDING_WORDS(60);
};
static_assert(sizeof(NvGraphicBuffer) == 0x16C, "NvGraphicBuffer has wrong size");
class GraphicBuffer final : public NvGraphicBuffer {
public:
explicit GraphicBuffer(u32 width, u32 height, PixelFormat format, u32 usage);
explicit GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
std::shared_ptr<NvGraphicBuffer> buffer);
~GraphicBuffer();
private:
Service::Nvidia::NvCore::NvMap* m_nvmap{};
};
static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size");
} // namespace Service::android

View File

@@ -431,7 +431,8 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) {
void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) {
u8 battery_percentage_flag{1};
LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag);
LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}",
battery_percentage_flag);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -491,29 +492,6 @@ void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) {
rb.PushEnum(ChineseTraditionalInputMethod::Unknown0);
}
void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "(STUBBED) called");
const HomeMenuScheme default_color = {
.main = 0xFF323232,
.back = 0xFF323232,
.sub = 0xFFFFFFFF,
.bezel = 0xFFFFFFFF,
.extra = 0xFF000000,
};
IPC::ResponseBuilder rb{ctx, 7};
rb.Push(ResultSuccess);
rb.PushRaw(default_color);
}
void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
LOG_WARNING(Service_SET, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(0);
}
void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) {
LOG_WARNING(Service_SET, "(STUBBED) called");
@@ -696,7 +674,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
{171, nullptr, "SetChineseTraditionalInputMethod"},
{172, nullptr, "GetPtmCycleCountReliability"},
{173, nullptr, "SetPtmCycleCountReliability"},
{174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"},
{174, nullptr, "GetHomeMenuScheme"},
{175, nullptr, "GetThemeSettings"},
{176, nullptr, "SetThemeSettings"},
{177, nullptr, "GetThemeKey"},
@@ -707,7 +685,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
{182, nullptr, "SetT"},
{183, nullptr, "GetPlatformRegion"},
{184, nullptr, "SetPlatformRegion"},
{185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
{185, nullptr, "GetHomeMenuSchemeModel"},
{186, nullptr, "GetMemoryUsageRateFlag"},
{187, nullptr, "GetTouchScreenMode"},
{188, nullptr, "SetTouchScreenMode"},

View File

@@ -269,16 +269,6 @@ private:
};
static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size");
/// This is nn::settings::system::HomeMenuScheme
struct HomeMenuScheme {
u32 main;
u32 back;
u32 sub;
u32 bezel;
u32 extra;
};
static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size");
void SetLanguageCode(HLERequestContext& ctx);
void GetFirmwareVersion(HLERequestContext& ctx);
void GetFirmwareVersion2(HLERequestContext& ctx);
@@ -315,8 +305,6 @@ private:
void GetKeyboardLayout(HLERequestContext& ctx);
void GetChineseTraditionalInputMethod(HLERequestContext& ctx);
void GetFieldTestingFlag(HLERequestContext& ctx);
void GetHomeMenuScheme(HLERequestContext& ctx);
void GetHomeMenuSchemeModel(HLERequestContext& ctx);
AccountSettings account_settings{
.flags = {},

View File

@@ -35,7 +35,7 @@ static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_cont
return {
buffer_queue_core,
std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)};
}
Display::Display(u64 id, std::string name_,

View File

@@ -231,7 +231,6 @@ add_library(shader_recompiler STATIC
ir_opt/rescaling_pass.cpp
ir_opt/ssa_rewrite_pass.cpp
ir_opt/texture_pass.cpp
ir_opt/vendor_workaround_pass.cpp
ir_opt/verification_pass.cpp
object_pool.h
precompiled_headers.h

View File

@@ -407,7 +407,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
}
ctx.AddCapability(spv::Capability::DemoteToHelperInvocation);
}
if (info.stores[IR::Attribute::ViewportIndex] && profile.support_multi_viewport) {
if (info.stores[IR::Attribute::ViewportIndex]) {
ctx.AddCapability(spv::Capability::MultiViewport);
}
if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {

View File

@@ -84,10 +84,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
}
return std::nullopt;
case IR::Attribute::ViewportIndex:
if (!ctx.profile.support_multi_viewport) {
LOG_WARNING(Shader, "Ignoring viewport index store on non-supporting driver");
return std::nullopt;
}
if (ctx.profile.support_viewport_index_layer_non_geometry ||
ctx.stage == Shader::Stage::Geometry) {
return OutAttr{ctx.viewport_index, ctx.U32[1]};

View File

@@ -310,7 +310,6 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
}
Optimization::CollectShaderInfoPass(env, program);
Optimization::LayerPass(program, host_info);
Optimization::VendorWorkaroundPass(program);
CollectInterpolationInfo(env, program);
AddNVNStorageBuffers(program);

View File

@@ -26,7 +26,6 @@ void SsaRewritePass(IR::Program& program);
void PositionPass(Environment& env, IR::Program& program);
void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);
void LayerPass(IR::Program& program, const HostTranslateInfo& host_info);
void VendorWorkaroundPass(IR::Program& program);
void VerificationPass(const IR::Program& program);
// Dual Vertex

View File

@@ -1,79 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#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 {
void AddingByteSwapsWorkaround(IR::Block& block, IR::Inst& inst) {
/*
* Workaround for an NVIDIA bug seen in Super Mario RPG
*
* We are looking for this pattern:
* %lhs_bfe = BitFieldUExtract %factor_a, #0, #16
* %lhs_mul = IMul32 %lhs_bfe, %factor_b // potentially optional?
* %lhs_shl = ShiftLeftLogical32 %lhs_mul, #16
* %rhs_bfe = BitFieldUExtract %factor_a, #16, #16
* %result = IAdd32 %lhs_shl, %rhs_bfe
*
* And replacing the IAdd32 with a BitwiseOr32
* %result = BitwiseOr32 %lhs_shl, %rhs_bfe
*
*/
IR::Inst* const lhs_shl{inst.Arg(0).TryInstRecursive()};
IR::Inst* const rhs_bfe{inst.Arg(1).TryInstRecursive()};
if (!lhs_shl || !rhs_bfe) {
return;
}
if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 ||
lhs_shl->Arg(1) != IR::Value{16U}) {
return;
}
if (rhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract || rhs_bfe->Arg(1) != IR::Value{16U} ||
rhs_bfe->Arg(2) != IR::Value{16U}) {
return;
}
IR::Inst* const lhs_mul{lhs_shl->Arg(0).TryInstRecursive()};
if (!lhs_mul) {
return;
}
const bool lhs_mul_optional{lhs_mul->GetOpcode() == IR::Opcode::BitFieldUExtract};
if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 &&
lhs_mul->GetOpcode() != IR::Opcode::BitFieldUExtract) {
return;
}
IR::Inst* const lhs_bfe{lhs_mul_optional ? lhs_mul : lhs_mul->Arg(0).TryInstRecursive()};
if (!lhs_bfe) {
return;
}
if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) {
return;
}
if (lhs_bfe->Arg(1) != IR::Value{0U} || lhs_bfe->Arg(2) != IR::Value{16U}) {
return;
}
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
inst.ReplaceUsesWith(ir.BitwiseOr(IR::U32{inst.Arg(0)}, IR::U32{inst.Arg(1)}));
}
} // Anonymous namespace
void VendorWorkaroundPass(IR::Program& program) {
for (IR::Block* const block : program.post_order_blocks) {
for (IR::Inst& inst : block->Instructions()) {
switch (inst.GetOpcode()) {
case IR::Opcode::IAdd32:
AddingByteSwapsWorkaround(*block, inst);
break;
default:
break;
}
}
}
}
} // namespace Shader::Optimization

View File

@@ -43,7 +43,6 @@ struct Profile {
bool support_gl_sparse_textures{};
bool support_gl_derivative_control{};
bool support_scaled_attributes{};
bool support_multi_viewport{};
bool warp_size_potentially_larger_than_guest{};

View File

@@ -4,7 +4,7 @@
add_subdirectory(host_shaders)
if(LIBVA_FOUND)
set_source_files_properties(host1x/ffmpeg/ffmpeg.cpp
set_source_files_properties(host1x/codecs/codec.cpp
PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
endif()
@@ -66,8 +66,6 @@ add_library(video_core STATIC
host1x/codecs/vp9.cpp
host1x/codecs/vp9.h
host1x/codecs/vp9_types.h
host1x/ffmpeg/ffmpeg.cpp
host1x/ffmpeg/ffmpeg.h
host1x/control.cpp
host1x/control.h
host1x/host1x.cpp

View File

@@ -1192,6 +1192,11 @@ void BufferCache<P>::UpdateDrawIndirect() {
.size = static_cast<u32>(size),
.buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
};
VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
IntervalType interval{cpu_addr_start, cpu_addr_end};
ClearDownload(interval);
common_ranges.subtract(interval);
};
if (current_draw_indirect->include_count) {
update(current_draw_indirect->count_start_address, sizeof(u32),

View File

@@ -268,7 +268,7 @@ size_t Maxwell3D::EstimateIndexBufferSize() {
std::numeric_limits<u32>::max()};
const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
const size_t log2_byte_size = Common::Log2Ceil64(byte_size);
const size_t cap{GetMaxCurrentVertices() * 4 * byte_size};
const size_t cap{GetMaxCurrentVertices() * 3 * byte_size};
const size_t lower_cap =
std::min<size_t>(static_cast<size_t>(end_address - start_address), cap);
return std::min<size_t>(

View File

@@ -1,7 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <fstream>
#include <vector>
#include "common/assert.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "video_core/host1x/codecs/codec.h"
#include "video_core/host1x/codecs/h264.h"
@@ -10,17 +14,242 @@
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
extern "C" {
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#ifdef LIBVA_FOUND
// for querying VAAPI driver information
#include <libavutil/hwcontext_vaapi.h>
#endif
}
namespace Tegra {
namespace {
constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
constexpr std::array PREFERRED_GPU_DECODERS = {
AV_HWDEVICE_TYPE_CUDA,
#ifdef _WIN32
AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_DXVA2,
#elif defined(__unix__)
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_VDPAU,
#endif
// last resort for Linux Flatpak (w/ NVIDIA)
AV_HWDEVICE_TYPE_VULKAN,
};
void AVPacketDeleter(AVPacket* ptr) {
av_packet_free(&ptr);
}
using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>;
AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) {
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
if (*p == av_codec_ctx->pix_fmt) {
return av_codec_ctx->pix_fmt;
}
}
LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
av_buffer_unref(&av_codec_ctx->hw_device_ctx);
av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
return PREFERRED_CPU_FMT;
}
// List all the currently available hwcontext in ffmpeg
std::vector<AVHWDeviceType> ListSupportedContexts() {
std::vector<AVHWDeviceType> contexts{};
AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
do {
current_device_type = av_hwdevice_iterate_types(current_device_type);
contexts.push_back(current_device_type);
} while (current_device_type != AV_HWDEVICE_TYPE_NONE);
return contexts;
}
} // namespace
void AVFrameDeleter(AVFrame* ptr) {
av_frame_free(&ptr);
}
Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs)
: host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)),
vp8_decoder(std::make_unique<Decoder::VP8>(host1x)),
vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {}
Codec::~Codec() = default;
Codec::~Codec() {
if (!initialized) {
return;
}
// Free libav memory
avcodec_free_context(&av_codec_ctx);
av_buffer_unref(&av_gpu_decoder);
if (filters_initialized) {
avfilter_graph_free(&av_filter_graph);
}
}
bool Codec::CreateGpuAvDevice() {
static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
static const auto supported_contexts = ListSupportedContexts();
for (const auto& type : PREFERRED_GPU_DECODERS) {
if (std::none_of(supported_contexts.begin(), supported_contexts.end(),
[&type](const auto& context) { return context == type; })) {
LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
continue;
}
// Avoid memory leak from not cleaning up after av_hwdevice_ctx_create
av_buffer_unref(&av_gpu_decoder);
const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
if (hwdevice_res < 0) {
LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
av_hwdevice_get_type_name(type), hwdevice_res);
continue;
}
#ifdef LIBVA_FOUND
if (type == AV_HWDEVICE_TYPE_VAAPI) {
// we need to determine if this is an impersonated VAAPI driver
AVHWDeviceContext* hwctx =
static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data));
AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
const char* vendor_name = vaQueryVendorString(vactx->display);
if (strstr(vendor_name, "VDPAU backend")) {
// VDPAU impersonated VAAPI impl's are super buggy, we need to skip them
LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver");
continue;
} else {
// according to some user testing, certain vaapi driver (Intel?) could be buggy
// so let's log the driver name which may help the developers/supporters
LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name);
}
}
#endif
for (int i = 0;; i++) {
const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
if (!config) {
LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.",
av_codec->name, av_hwdevice_get_type_name(type));
break;
}
if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
av_codec_ctx->pix_fmt = config->pix_fmt;
return true;
}
}
}
return false;
}
void Codec::InitializeAvCodecContext() {
av_codec_ctx = avcodec_alloc_context3(av_codec);
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
av_codec_ctx->thread_count = 0;
av_codec_ctx->thread_type &= ~FF_THREAD_FRAME;
}
void Codec::InitializeGpuDecoder() {
if (!CreateGpuAvDevice()) {
av_buffer_unref(&av_gpu_decoder);
return;
}
auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder);
ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
av_codec_ctx->hw_device_ctx = hw_device_ctx;
av_codec_ctx->get_format = GetGpuFormat;
}
void Codec::InitializeAvFilters(AVFrame* frame) {
const AVFilter* buffer_src = avfilter_get_by_name("buffer");
const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
AVFilterInOut* inputs = avfilter_inout_alloc();
AVFilterInOut* outputs = avfilter_inout_alloc();
SCOPE_EXIT({
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
});
// Don't know how to get the accurate time_base but it doesn't matter for yadif filter
// so just use 1/1 to make buffer filter happy
std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame->width,
frame->height, frame->format);
av_filter_graph = avfilter_graph_alloc();
int ret = avfilter_graph_create_filter(&av_filter_src_ctx, buffer_src, "in", args.c_str(),
nullptr, av_filter_graph);
if (ret < 0) {
LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter source error: {}", ret);
return;
}
ret = avfilter_graph_create_filter(&av_filter_sink_ctx, buffer_sink, "out", nullptr, nullptr,
av_filter_graph);
if (ret < 0) {
LOG_ERROR(Service_NVDRV, "avfilter_graph_create_filter sink error: {}", ret);
return;
}
inputs->name = av_strdup("out");
inputs->filter_ctx = av_filter_sink_ctx;
inputs->pad_idx = 0;
inputs->next = nullptr;
outputs->name = av_strdup("in");
outputs->filter_ctx = av_filter_src_ctx;
outputs->pad_idx = 0;
outputs->next = nullptr;
const char* description = "yadif=1:-1:0";
ret = avfilter_graph_parse_ptr(av_filter_graph, description, &inputs, &outputs, nullptr);
if (ret < 0) {
LOG_ERROR(Service_NVDRV, "avfilter_graph_parse_ptr error: {}", ret);
return;
}
ret = avfilter_graph_config(av_filter_graph, nullptr);
if (ret < 0) {
LOG_ERROR(Service_NVDRV, "avfilter_graph_config error: {}", ret);
return;
}
filters_initialized = true;
}
void Codec::Initialize() {
initialized = decode_api.Initialize(current_codec);
const AVCodecID codec = [&] {
switch (current_codec) {
case Host1x::NvdecCommon::VideoCodec::H264:
return AV_CODEC_ID_H264;
case Host1x::NvdecCommon::VideoCodec::VP8:
return AV_CODEC_ID_VP8;
case Host1x::NvdecCommon::VideoCodec::VP9:
return AV_CODEC_ID_VP9;
default:
UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
return AV_CODEC_ID_NONE;
}
}();
av_codec = avcodec_find_decoder(codec);
InitializeAvCodecContext();
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
InitializeGpuDecoder();
}
if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) {
LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res);
avcodec_free_context(&av_codec_ctx);
av_buffer_unref(&av_gpu_decoder);
return;
}
if (!av_codec_ctx->hw_device_ctx) {
LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
}
initialized = true;
}
void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
@@ -35,18 +264,14 @@ void Codec::Decode() {
if (is_first_frame) {
Initialize();
}
if (!initialized) {
return;
}
// Assemble bitstream.
bool vp9_hidden_frame = false;
size_t configuration_size = 0;
const auto packet_data = [&]() {
const auto& frame_data = [&]() {
switch (current_codec) {
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame);
return h264_decoder->ComposeFrame(state, is_first_frame);
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
return vp8_decoder->ComposeFrame(state);
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
@@ -58,35 +283,89 @@ void Codec::Decode() {
return std::span<const u8>{};
}
}();
// Send assembled bitstream to decoder.
if (!decode_api.SendPacket(packet_data, configuration_size)) {
AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter};
if (!packet) {
LOG_ERROR(Service_NVDRV, "av_packet_alloc failed");
return;
}
// Only receive/store visible frames.
packet->data = const_cast<u8*>(frame_data.data());
packet->size = static_cast<s32>(frame_data.size());
if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) {
LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res);
return;
}
// Only receive/store visible frames
if (vp9_hidden_frame) {
return;
}
AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter};
AVFramePtr final_frame{nullptr, AVFrameDeleter};
ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed");
if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) {
LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
return;
}
if (initial_frame->width == 0 || initial_frame->height == 0) {
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
return;
}
bool is_interlaced = initial_frame->interlaced_frame != 0;
if (av_codec_ctx->hw_device_ctx) {
final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
// Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
// because Intel drivers crash unless using AV_PIX_FMT_NV12
final_frame->format = PREFERRED_GPU_FMT;
const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0);
ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret);
} else {
final_frame = std::move(initial_frame);
}
if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) {
UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
return;
}
if (!is_interlaced) {
av_frames.push(std::move(final_frame));
} else {
if (!filters_initialized) {
InitializeAvFilters(final_frame.get());
}
if (const int ret = av_buffersrc_add_frame_flags(av_filter_src_ctx, final_frame.get(),
AV_BUFFERSRC_FLAG_KEEP_REF);
ret) {
LOG_DEBUG(Service_NVDRV, "av_buffersrc_add_frame_flags error {}", ret);
return;
}
while (true) {
auto filter_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
// Receive output frames from decoder.
decode_api.ReceiveFrames(frames);
int ret = av_buffersink_get_frame(av_filter_sink_ctx, filter_frame.get());
while (frames.size() > 10) {
LOG_DEBUG(HW_GPU, "ReceiveFrames overflow, dropped frame");
frames.pop();
if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF))
break;
if (ret < 0) {
LOG_DEBUG(Service_NVDRV, "av_buffersink_get_frame error {}", ret);
return;
}
av_frames.push(std::move(filter_frame));
}
}
while (av_frames.size() > 10) {
LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
av_frames.pop();
}
}
std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() {
AVFramePtr Codec::GetCurrentFrame() {
// Sometimes VIC will request more frames than have been decoded.
// in this case, return a blank frame and don't overwrite previous data.
if (frames.empty()) {
return {};
// in this case, return a nullptr and don't overwrite previous frame data
if (av_frames.empty()) {
return AVFramePtr{nullptr, AVFrameDeleter};
}
auto frame = std::move(frames.front());
frames.pop();
AVFramePtr frame = std::move(av_frames.front());
av_frames.pop();
return frame;
}

View File

@@ -4,15 +4,28 @@
#pragma once
#include <memory>
#include <optional>
#include <string_view>
#include <queue>
#include "common/common_types.h"
#include "video_core/host1x/ffmpeg/ffmpeg.h"
#include "video_core/host1x/nvdec_common.h"
extern "C" {
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
}
namespace Tegra {
void AVFrameDeleter(AVFrame* ptr);
using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>;
namespace Decoder {
class H264;
class VP8;
@@ -38,7 +51,7 @@ public:
void Decode();
/// Returns next decoded frame
[[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame();
[[nodiscard]] AVFramePtr GetCurrentFrame();
/// Returns the value of current_codec
[[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const;
@@ -47,9 +60,25 @@ public:
[[nodiscard]] std::string_view GetCurrentCodecName() const;
private:
void InitializeAvCodecContext();
void InitializeAvFilters(AVFrame* frame);
void InitializeGpuDecoder();
bool CreateGpuAvDevice();
bool initialized{};
bool filters_initialized{};
Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None};
FFmpeg::DecodeApi decode_api;
const AVCodec* av_codec{nullptr};
AVCodecContext* av_codec_ctx{nullptr};
AVBufferRef* av_gpu_decoder{nullptr};
AVFilterContext* av_filter_src_ctx{nullptr};
AVFilterContext* av_filter_sink_ctx{nullptr};
AVFilterGraph* av_filter_graph{nullptr};
Host1x::Host1x& host1x;
const Host1x::NvdecCommon::NvdecRegisters& state;
@@ -57,7 +86,7 @@ private:
std::unique_ptr<Decoder::VP8> vp8_decoder;
std::unique_ptr<Decoder::VP9> vp9_decoder;
std::queue<std::unique_ptr<FFmpeg::Frame>> frames{};
std::queue<AVFramePtr> av_frames{};
};
} // namespace Tegra

View File

@@ -30,7 +30,7 @@ H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {}
H264::~H264() = default;
std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
size_t* out_configuration_size, bool is_first_frame) {
bool is_first_frame) {
H264DecoderContext context;
host1x.MemoryManager().ReadBlock(state.picture_info_offset, &context,
sizeof(H264DecoderContext));
@@ -39,7 +39,6 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
if (!is_first_frame && frame_number != 0) {
frame.resize_destructive(context.stream_len);
host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
*out_configuration_size = 0;
return frame;
}
@@ -158,7 +157,6 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
frame.resize(encoded_header.size() + context.stream_len);
std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
*out_configuration_size = encoded_header.size();
host1x.MemoryManager().ReadBlock(state.frame_bitstream_offset,
frame.data() + encoded_header.size(), context.stream_len);

View File

@@ -67,7 +67,6 @@ public:
/// Compose the H264 frame for FFmpeg decoding
[[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
size_t* out_configuration_size,
bool is_first_frame = false);
private:

View File

@@ -1,419 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "video_core/host1x/ffmpeg/ffmpeg.h"
extern "C" {
#ifdef LIBVA_FOUND
// for querying VAAPI driver information
#include <libavutil/hwcontext_vaapi.h>
#endif
}
namespace FFmpeg {
namespace {
constexpr AVPixelFormat PreferredGpuFormat = AV_PIX_FMT_NV12;
constexpr AVPixelFormat PreferredCpuFormat = AV_PIX_FMT_YUV420P;
constexpr std::array PreferredGpuDecoders = {
AV_HWDEVICE_TYPE_CUDA,
#ifdef _WIN32
AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_DXVA2,
#elif defined(__unix__)
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_VDPAU,
#endif
// last resort for Linux Flatpak (w/ NVIDIA)
AV_HWDEVICE_TYPE_VULKAN,
};
AVPixelFormat GetGpuFormat(AVCodecContext* codec_context, const AVPixelFormat* pix_fmts) {
for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
if (*p == codec_context->pix_fmt) {
return codec_context->pix_fmt;
}
}
LOG_INFO(HW_GPU, "Could not find compatible GPU AV format, falling back to CPU");
av_buffer_unref(&codec_context->hw_device_ctx);
codec_context->pix_fmt = PreferredCpuFormat;
return codec_context->pix_fmt;
}
std::string AVError(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE] = {};
av_make_error_string(errbuf, sizeof(errbuf) - 1, errnum);
return errbuf;
}
} // namespace
Packet::Packet(std::span<const u8> data) {
m_packet = av_packet_alloc();
m_packet->data = const_cast<u8*>(data.data());
m_packet->size = static_cast<s32>(data.size());
}
Packet::~Packet() {
av_packet_free(&m_packet);
}
Frame::Frame() {
m_frame = av_frame_alloc();
}
Frame::~Frame() {
av_frame_free(&m_frame);
}
Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
const AVCodecID av_codec = [&] {
switch (codec) {
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
return AV_CODEC_ID_H264;
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
return AV_CODEC_ID_VP8;
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
return AV_CODEC_ID_VP9;
default:
UNIMPLEMENTED_MSG("Unknown codec {}", codec);
return AV_CODEC_ID_NONE;
}
}();
m_codec = avcodec_find_decoder(av_codec);
}
bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
for (int i = 0;; i++) {
const AVCodecHWConfig* config = avcodec_get_hw_config(m_codec, i);
if (!config) {
LOG_DEBUG(HW_GPU, "{} decoder does not support device type {}", m_codec->name,
av_hwdevice_get_type_name(type));
break;
}
if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) != 0 &&
config->device_type == type) {
LOG_INFO(HW_GPU, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
*out_pix_fmt = config->pix_fmt;
return true;
}
}
return false;
}
std::vector<AVHWDeviceType> HardwareContext::GetSupportedDeviceTypes() {
std::vector<AVHWDeviceType> types;
AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
while (true) {
current_device_type = av_hwdevice_iterate_types(current_device_type);
if (current_device_type == AV_HWDEVICE_TYPE_NONE) {
return types;
}
types.push_back(current_device_type);
}
}
HardwareContext::~HardwareContext() {
av_buffer_unref(&m_gpu_decoder);
}
bool HardwareContext::InitializeForDecoder(DecoderContext& decoder_context,
const Decoder& decoder) {
const auto supported_types = GetSupportedDeviceTypes();
for (const auto type : PreferredGpuDecoders) {
AVPixelFormat hw_pix_fmt;
if (std::ranges::find(supported_types, type) == supported_types.end()) {
LOG_DEBUG(HW_GPU, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
continue;
}
if (!this->InitializeWithType(type)) {
continue;
}
if (decoder.SupportsDecodingOnDevice(&hw_pix_fmt, type)) {
decoder_context.InitializeHardwareDecoder(*this, hw_pix_fmt);
return true;
}
}
return false;
}
bool HardwareContext::InitializeWithType(AVHWDeviceType type) {
av_buffer_unref(&m_gpu_decoder);
if (const int ret = av_hwdevice_ctx_create(&m_gpu_decoder, type, nullptr, nullptr, 0);
ret < 0) {
LOG_DEBUG(HW_GPU, "av_hwdevice_ctx_create({}) failed: {}", av_hwdevice_get_type_name(type),
AVError(ret));
return false;
}
#ifdef LIBVA_FOUND
if (type == AV_HWDEVICE_TYPE_VAAPI) {
// We need to determine if this is an impersonated VAAPI driver.
auto* hwctx = reinterpret_cast<AVHWDeviceContext*>(m_gpu_decoder->data);
auto* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
const char* vendor_name = vaQueryVendorString(vactx->display);
if (strstr(vendor_name, "VDPAU backend")) {
// VDPAU impersonated VAAPI impls are super buggy, we need to skip them.
LOG_DEBUG(HW_GPU, "Skipping VDPAU impersonated VAAPI driver");
return false;
} else {
// According to some user testing, certain VAAPI drivers (Intel?) could be buggy.
// Log the driver name just in case.
LOG_DEBUG(HW_GPU, "Using VAAPI driver: {}", vendor_name);
}
}
#endif
return true;
}
DecoderContext::DecoderContext(const Decoder& decoder) {
m_codec_context = avcodec_alloc_context3(decoder.GetCodec());
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
m_codec_context->thread_count = 0;
m_codec_context->thread_type &= ~FF_THREAD_FRAME;
}
DecoderContext::~DecoderContext() {
av_buffer_unref(&m_codec_context->hw_device_ctx);
avcodec_free_context(&m_codec_context);
}
void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context,
AVPixelFormat hw_pix_fmt) {
m_codec_context->hw_device_ctx = av_buffer_ref(context.GetBufferRef());
m_codec_context->get_format = GetGpuFormat;
m_codec_context->pix_fmt = hw_pix_fmt;
}
bool DecoderContext::OpenContext(const Decoder& decoder) {
if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret));
return false;
}
if (!m_codec_context->hw_device_ctx) {
LOG_INFO(HW_GPU, "Using FFmpeg software decoding");
}
return true;
}
bool DecoderContext::SendPacket(const Packet& packet) {
if (const int ret = avcodec_send_packet(m_codec_context, packet.GetPacket()); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_send_packet error: {}", AVError(ret));
return false;
}
return true;
}
std::unique_ptr<Frame> DecoderContext::ReceiveFrame(bool* out_is_interlaced) {
auto dst_frame = std::make_unique<Frame>();
const auto ReceiveImpl = [&](AVFrame* frame) {
if (const int ret = avcodec_receive_frame(m_codec_context, frame); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_receive_frame error: {}", AVError(ret));
return false;
}
*out_is_interlaced = frame->interlaced_frame != 0;
return true;
};
if (m_codec_context->hw_device_ctx) {
// If we have a hardware context, make a separate frame here to receive the
// hardware result before sending it to the output.
Frame intermediate_frame;
if (!ReceiveImpl(intermediate_frame.GetFrame())) {
return {};
}
dst_frame->SetFormat(PreferredGpuFormat);
if (const int ret =
av_hwframe_transfer_data(dst_frame->GetFrame(), intermediate_frame.GetFrame(), 0);
ret < 0) {
LOG_ERROR(HW_GPU, "av_hwframe_transfer_data error: {}", AVError(ret));
return {};
}
} else {
// Otherwise, decode the frame as normal.
if (!ReceiveImpl(dst_frame->GetFrame())) {
return {};
}
}
return dst_frame;
}
DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) {
const AVFilter* buffer_src = avfilter_get_by_name("buffer");
const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
AVFilterInOut* inputs = avfilter_inout_alloc();
AVFilterInOut* outputs = avfilter_inout_alloc();
SCOPE_EXIT({
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
});
// Don't know how to get the accurate time_base but it doesn't matter for yadif filter
// so just use 1/1 to make buffer filter happy
std::string args = fmt::format("video_size={}x{}:pix_fmt={}:time_base=1/1", frame.GetWidth(),
frame.GetHeight(), static_cast<int>(frame.GetPixelFormat()));
m_filter_graph = avfilter_graph_alloc();
int ret = avfilter_graph_create_filter(&m_source_context, buffer_src, "in", args.c_str(),
nullptr, m_filter_graph);
if (ret < 0) {
LOG_ERROR(HW_GPU, "avfilter_graph_create_filter source error: {}", AVError(ret));
return;
}
ret = avfilter_graph_create_filter(&m_sink_context, buffer_sink, "out", nullptr, nullptr,
m_filter_graph);
if (ret < 0) {
LOG_ERROR(HW_GPU, "avfilter_graph_create_filter sink error: {}", AVError(ret));
return;
}
inputs->name = av_strdup("out");
inputs->filter_ctx = m_sink_context;
inputs->pad_idx = 0;
inputs->next = nullptr;
outputs->name = av_strdup("in");
outputs->filter_ctx = m_source_context;
outputs->pad_idx = 0;
outputs->next = nullptr;
const char* description = "yadif=1:-1:0";
ret = avfilter_graph_parse_ptr(m_filter_graph, description, &inputs, &outputs, nullptr);
if (ret < 0) {
LOG_ERROR(HW_GPU, "avfilter_graph_parse_ptr error: {}", AVError(ret));
return;
}
ret = avfilter_graph_config(m_filter_graph, nullptr);
if (ret < 0) {
LOG_ERROR(HW_GPU, "avfilter_graph_config error: {}", AVError(ret));
return;
}
m_initialized = true;
}
bool DeinterlaceFilter::AddSourceFrame(const Frame& frame) {
if (const int ret = av_buffersrc_add_frame_flags(m_source_context, frame.GetFrame(),
AV_BUFFERSRC_FLAG_KEEP_REF);
ret < 0) {
LOG_ERROR(HW_GPU, "av_buffersrc_add_frame_flags error: {}", AVError(ret));
return false;
}
return true;
}
std::unique_ptr<Frame> DeinterlaceFilter::DrainSinkFrame() {
auto dst_frame = std::make_unique<Frame>();
const int ret = av_buffersink_get_frame(m_sink_context, dst_frame->GetFrame());
if (ret == AVERROR(EAGAIN) || ret == AVERROR(AVERROR_EOF)) {
return {};
}
if (ret < 0) {
LOG_ERROR(HW_GPU, "av_buffersink_get_frame error: {}", AVError(ret));
return {};
}
return dst_frame;
}
DeinterlaceFilter::~DeinterlaceFilter() {
avfilter_graph_free(&m_filter_graph);
}
void DecodeApi::Reset() {
m_deinterlace_filter.reset();
m_hardware_context.reset();
m_decoder_context.reset();
m_decoder.reset();
}
bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
this->Reset();
m_decoder.emplace(codec);
m_decoder_context.emplace(*m_decoder);
// Enable GPU decoding if requested.
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
m_hardware_context.emplace();
m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
}
// Open the decoder context.
if (!m_decoder_context->OpenContext(*m_decoder)) {
this->Reset();
return false;
}
return true;
}
bool DecodeApi::SendPacket(std::span<const u8> packet_data, size_t configuration_size) {
FFmpeg::Packet packet(packet_data);
return m_decoder_context->SendPacket(packet);
}
void DecodeApi::ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue) {
// Receive raw frame from decoder.
bool is_interlaced;
auto frame = m_decoder_context->ReceiveFrame(&is_interlaced);
if (!frame) {
return;
}
if (!is_interlaced) {
// If the frame is not interlaced, we can pend it now.
frame_queue.push(std::move(frame));
} else {
// Create the deinterlacer if needed.
if (!m_deinterlace_filter) {
m_deinterlace_filter.emplace(*frame);
}
// Add the frame we just received.
if (!m_deinterlace_filter->AddSourceFrame(*frame)) {
return;
}
// Pend output fields.
while (true) {
auto filter_frame = m_deinterlace_filter->DrainSinkFrame();
if (!filter_frame) {
break;
}
frame_queue.push(std::move(filter_frame));
}
}
}
} // namespace FFmpeg

View File

@@ -1,213 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <optional>
#include <span>
#include <vector>
#include <queue>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/host1x/nvdec_common.h"
extern "C" {
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
}
namespace FFmpeg {
class Packet;
class Frame;
class Decoder;
class HardwareContext;
class DecoderContext;
class DeinterlaceFilter;
// Wraps an AVPacket, a container for compressed bitstream data.
class Packet {
public:
YUZU_NON_COPYABLE(Packet);
YUZU_NON_MOVEABLE(Packet);
explicit Packet(std::span<const u8> data);
~Packet();
AVPacket* GetPacket() const {
return m_packet;
}
private:
AVPacket* m_packet{};
};
// Wraps an AVFrame, a container for audio and video stream data.
class Frame {
public:
YUZU_NON_COPYABLE(Frame);
YUZU_NON_MOVEABLE(Frame);
explicit Frame();
~Frame();
int GetWidth() const {
return m_frame->width;
}
int GetHeight() const {
return m_frame->height;
}
AVPixelFormat GetPixelFormat() const {
return static_cast<AVPixelFormat>(m_frame->format);
}
int GetStride(int plane) const {
return m_frame->linesize[plane];
}
int* GetStrides() const {
return m_frame->linesize;
}
u8* GetData(int plane) const {
return m_frame->data[plane];
}
u8** GetPlanes() const {
return m_frame->data;
}
void SetFormat(int format) {
m_frame->format = format;
}
AVFrame* GetFrame() const {
return m_frame;
}
private:
AVFrame* m_frame{};
};
// Wraps an AVCodec, a type containing information about a codec.
class Decoder {
public:
YUZU_NON_COPYABLE(Decoder);
YUZU_NON_MOVEABLE(Decoder);
explicit Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec);
~Decoder() = default;
bool SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const;
const AVCodec* GetCodec() const {
return m_codec;
}
private:
const AVCodec* m_codec{};
};
// Wraps AVBufferRef for an accelerated decoder.
class HardwareContext {
public:
YUZU_NON_COPYABLE(HardwareContext);
YUZU_NON_MOVEABLE(HardwareContext);
static std::vector<AVHWDeviceType> GetSupportedDeviceTypes();
explicit HardwareContext() = default;
~HardwareContext();
bool InitializeForDecoder(DecoderContext& decoder_context, const Decoder& decoder);
AVBufferRef* GetBufferRef() const {
return m_gpu_decoder;
}
private:
bool InitializeWithType(AVHWDeviceType type);
AVBufferRef* m_gpu_decoder{};
};
// Wraps an AVCodecContext.
class DecoderContext {
public:
YUZU_NON_COPYABLE(DecoderContext);
YUZU_NON_MOVEABLE(DecoderContext);
explicit DecoderContext(const Decoder& decoder);
~DecoderContext();
void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
bool OpenContext(const Decoder& decoder);
bool SendPacket(const Packet& packet);
std::unique_ptr<Frame> ReceiveFrame(bool* out_is_interlaced);
AVCodecContext* GetCodecContext() const {
return m_codec_context;
}
private:
AVCodecContext* m_codec_context{};
};
// Wraps an AVFilterGraph.
class DeinterlaceFilter {
public:
YUZU_NON_COPYABLE(DeinterlaceFilter);
YUZU_NON_MOVEABLE(DeinterlaceFilter);
explicit DeinterlaceFilter(const Frame& frame);
~DeinterlaceFilter();
bool AddSourceFrame(const Frame& frame);
std::unique_ptr<Frame> DrainSinkFrame();
private:
AVFilterGraph* m_filter_graph{};
AVFilterContext* m_source_context{};
AVFilterContext* m_sink_context{};
bool m_initialized{};
};
class DecodeApi {
public:
YUZU_NON_COPYABLE(DecodeApi);
YUZU_NON_MOVEABLE(DecodeApi);
DecodeApi() = default;
~DecodeApi() = default;
bool Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec);
void Reset();
bool SendPacket(std::span<const u8> packet_data, size_t configuration_size);
void ReceiveFrames(std::queue<std::unique_ptr<Frame>>& frame_queue);
private:
std::optional<FFmpeg::Decoder> m_decoder;
std::optional<FFmpeg::DecoderContext> m_decoder_context;
std::optional<FFmpeg::HardwareContext> m_hardware_context;
std::optional<FFmpeg::DeinterlaceFilter> m_deinterlace_filter;
};
} // namespace FFmpeg

View File

@@ -28,7 +28,7 @@ void Nvdec::ProcessMethod(u32 method, u32 argument) {
}
}
std::unique_ptr<FFmpeg::Frame> Nvdec::GetFrame() {
AVFramePtr Nvdec::GetFrame() {
return codec->GetCurrentFrame();
}

View File

@@ -23,7 +23,7 @@ public:
void ProcessMethod(u32 method, u32 argument);
/// Return most recently decoded frame
[[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetFrame();
[[nodiscard]] AVFramePtr GetFrame();
private:
/// Invoke codec to decode a frame

View File

@@ -82,26 +82,27 @@ void Vic::Execute() {
return;
}
const VicConfig config{host1x.MemoryManager().Read<u64>(config_struct_address + 0x20)};
auto frame = nvdec_processor->GetFrame();
const AVFramePtr frame_ptr = nvdec_processor->GetFrame();
const auto* frame = frame_ptr.get();
if (!frame) {
return;
}
const u64 surface_width = config.surface_width_minus1 + 1;
const u64 surface_height = config.surface_height_minus1 + 1;
if (static_cast<u64>(frame->GetWidth()) != surface_width ||
static_cast<u64>(frame->GetHeight()) != surface_height) {
if (static_cast<u64>(frame->width) != surface_width ||
static_cast<u64>(frame->height) != surface_height) {
// TODO: Properly support multiple video streams with differing frame dimensions
LOG_WARNING(Service_NVDRV, "Frame dimensions {}x{} don't match surface dimensions {}x{}",
frame->GetWidth(), frame->GetHeight(), surface_width, surface_height);
frame->width, frame->height, surface_width, surface_height);
}
switch (config.pixel_format) {
case VideoPixelFormat::RGBA8:
case VideoPixelFormat::BGRA8:
case VideoPixelFormat::RGBX8:
WriteRGBFrame(std::move(frame), config);
WriteRGBFrame(frame, config);
break;
case VideoPixelFormat::YUV420:
WriteYUVFrame(std::move(frame), config);
WriteYUVFrame(frame, config);
break;
default:
UNIMPLEMENTED_MSG("Unknown video pixel format {:X}", config.pixel_format.Value());
@@ -109,14 +110,10 @@ void Vic::Execute() {
}
}
void Vic::WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) {
void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
LOG_TRACE(Service_NVDRV, "Writing RGB Frame");
const auto frame_width = frame->GetWidth();
const auto frame_height = frame->GetHeight();
const auto frame_format = frame->GetPixelFormat();
if (!scaler_ctx || frame_width != scaler_width || frame_height != scaler_height) {
if (!scaler_ctx || frame->width != scaler_width || frame->height != scaler_height) {
const AVPixelFormat target_format = [pixel_format = config.pixel_format]() {
switch (pixel_format) {
case VideoPixelFormat::RGBA8:
@@ -132,26 +129,27 @@ void Vic::WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& c
sws_freeContext(scaler_ctx);
// Frames are decoded into either YUV420 or NV12 formats. Convert to desired RGB format
scaler_ctx = sws_getContext(frame_width, frame_height, frame_format, frame_width,
frame_height, target_format, 0, nullptr, nullptr, nullptr);
scaler_width = frame_width;
scaler_height = frame_height;
scaler_ctx = sws_getContext(frame->width, frame->height,
static_cast<AVPixelFormat>(frame->format), frame->width,
frame->height, target_format, 0, nullptr, nullptr, nullptr);
scaler_width = frame->width;
scaler_height = frame->height;
converted_frame_buffer.reset();
}
if (!converted_frame_buffer) {
const size_t frame_size = frame_width * frame_height * 4;
const size_t frame_size = frame->width * frame->height * 4;
converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(frame_size)), av_free};
}
const std::array<int, 4> converted_stride{frame_width * 4, frame_height * 4, 0, 0};
const std::array<int, 4> converted_stride{frame->width * 4, frame->height * 4, 0, 0};
u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
sws_scale(scaler_ctx, frame->GetPlanes(), frame->GetStrides(), 0, frame_height,
&converted_frame_buf_addr, converted_stride.data());
sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height, &converted_frame_buf_addr,
converted_stride.data());
// Use the minimum of surface/frame dimensions to avoid buffer overflow.
const u32 surface_width = static_cast<u32>(config.surface_width_minus1) + 1;
const u32 surface_height = static_cast<u32>(config.surface_height_minus1) + 1;
const u32 width = std::min(surface_width, static_cast<u32>(frame_width));
const u32 height = std::min(surface_height, static_cast<u32>(frame_height));
const u32 width = std::min(surface_width, static_cast<u32>(frame->width));
const u32 height = std::min(surface_height, static_cast<u32>(frame->height));
const u32 blk_kind = static_cast<u32>(config.block_linear_kind);
if (blk_kind != 0) {
// swizzle pitch linear to block linear
@@ -171,23 +169,23 @@ void Vic::WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& c
}
}
void Vic::WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config) {
void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame");
const std::size_t surface_width = config.surface_width_minus1 + 1;
const std::size_t surface_height = config.surface_height_minus1 + 1;
const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL;
// Use the minimum of surface/frame dimensions to avoid buffer overflow.
const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->GetWidth()));
const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->GetHeight()));
const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width));
const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height));
const auto stride = static_cast<size_t>(frame->GetStride(0));
const auto stride = static_cast<size_t>(frame->linesize[0]);
luma_buffer.resize_destructive(aligned_width * surface_height);
chroma_buffer.resize_destructive(aligned_width * surface_height / 2);
// Populate luma buffer
const u8* luma_src = frame->GetData(0);
const u8* luma_src = frame->data[0];
for (std::size_t y = 0; y < frame_height; ++y) {
const std::size_t src = y * stride;
const std::size_t dst = y * aligned_width;
@@ -198,16 +196,16 @@ void Vic::WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& c
// Chroma
const std::size_t half_height = frame_height / 2;
const auto half_stride = static_cast<size_t>(frame->GetStride(1));
const auto half_stride = static_cast<size_t>(frame->linesize[1]);
switch (frame->GetPixelFormat()) {
switch (frame->format) {
case AV_PIX_FMT_YUV420P: {
// Frame from FFmpeg software
// Populate chroma buffer from both channels with interleaving.
const std::size_t half_width = frame_width / 2;
u8* chroma_buffer_data = chroma_buffer.data();
const u8* chroma_b_src = frame->GetData(1);
const u8* chroma_r_src = frame->GetData(2);
const u8* chroma_b_src = frame->data[1];
const u8* chroma_r_src = frame->data[2];
for (std::size_t y = 0; y < half_height; ++y) {
const std::size_t src = y * half_stride;
const std::size_t dst = y * aligned_width;
@@ -221,7 +219,7 @@ void Vic::WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& c
case AV_PIX_FMT_NV12: {
// Frame from VA-API hardware
// This is already interleaved so just copy
const u8* chroma_src = frame->GetData(1);
const u8* chroma_src = frame->data[1];
for (std::size_t y = 0; y < half_height; ++y) {
const std::size_t src = y * stride;
const std::size_t dst = y * aligned_width;

View File

@@ -39,9 +39,9 @@ public:
private:
void Execute();
void WriteRGBFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config);
void WriteRGBFrame(const AVFrame* frame, const VicConfig& config);
void WriteYUVFrame(std::unique_ptr<FFmpeg::Frame> frame, const VicConfig& config);
void WriteYUVFrame(const AVFrame* frame, const VicConfig& config);
Host1x& host1x;
std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor;

View File

@@ -559,9 +559,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
}
void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
const GLenum buffer_mode =
num_xfb_buffers_active == 1 ? GL_INTERLEAVED_ATTRIBS : GL_SEPARATE_ATTRIBS;
glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), buffer_mode);
glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS);
}
void GraphicsPipeline::GenerateTransformFeedbackState() {
@@ -569,14 +567,12 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
// when this is required.
GLint* cursor{xfb_attribs.data()};
num_xfb_buffers_active = 0;
for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
const auto& layout = key.xfb_state.layouts[feedback];
UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding");
if (layout.varying_count == 0) {
continue;
}
num_xfb_buffers_active++;
const auto& locations = key.xfb_state.varyings[feedback];
std::optional<u32> current_index;

View File

@@ -154,7 +154,6 @@ private:
static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
GLsizei num_xfb_attribs{};
u32 num_xfb_buffers_active{};
std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{};
std::mutex built_mutex;

View File

@@ -263,22 +263,6 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
info.y_negate = key.state.y_negate != 0;
return info;
}
size_t GetTotalPipelineWorkers() {
const size_t max_core_threads =
std::max<size_t>(static_cast<size_t>(std::thread::hardware_concurrency()), 2ULL) - 1ULL;
#ifdef ANDROID
// Leave at least a few cores free in android
constexpr size_t free_cores = 3ULL;
if (max_core_threads <= free_cores) {
return 1ULL;
}
return max_core_threads - free_cores;
#else
return max_core_threads;
#endif
}
} // Anonymous namespace
size_t ComputePipelineCacheKey::Hash() const noexcept {
@@ -310,8 +294,11 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
texture_cache{texture_cache_}, shader_notify{shader_notify_},
use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()},
use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()},
workers(device.HasBrokenParallelShaderCompiling() ? 1ULL : GetTotalPipelineWorkers(),
"VkPipelineBuilder"),
#ifdef ANDROID
workers(1, "VkPipelineBuilder"),
#else
workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"),
#endif
serialization_thread(1, "VkPipelineSerialization") {
const auto& float_control{device.FloatControlProperties()};
const VkDriverId driver_id{device.GetDriverID()};
@@ -351,7 +338,6 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
.support_native_ndc = device.IsExtDepthClipControlSupported(),
.support_scaled_attributes = !device.MustEmulateScaledFormats(),
.support_multi_viewport = device.SupportsMultiViewport(),
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),

View File

@@ -211,13 +211,6 @@ public:
return;
}
PauseCounter();
const auto driver_id = device.GetDriverID();
if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
pending_sync.clear();
sync_values_stash.clear();
return;
}
sync_values_stash.clear();
sync_values_stash.emplace_back();
std::vector<HostSyncValues>* sync_values = &sync_values_stash.back();
@@ -1385,12 +1378,6 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
return true;
}
auto driver_id = impl->device.GetDriverID();
if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
return true;
}
for (size_t i = 0; i < 2; i++) {
is_null[i] = !is_in_ac[i] && check_value(objects[i]->address);
}

View File

@@ -635,12 +635,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
has_broken_cube_compatibility = true;
}
}
if (is_qualcomm) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) {
has_broken_parallel_compiling = true;
}
}
if (extensions.sampler_filter_minmax && is_amd) {
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
if (!features.shader_float16_int8.shaderFloat16) {

View File

@@ -102,7 +102,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \
EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \
@@ -600,11 +599,6 @@ public:
return has_broken_cube_compatibility;
}
/// Returns true if parallel shader compiling has issues with the current driver.
bool HasBrokenParallelShaderCompiling() const {
return has_broken_parallel_compiling;
}
/// Returns the vendor name reported from Vulkan.
std::string_view GetVendorName() const {
return properties.driver.driverName;
@@ -669,10 +663,6 @@ public:
return supports_conditional_barriers;
}
bool SupportsMultiViewport() const {
return features2.features.multiViewport;
}
[[nodiscard]] static constexpr bool CheckBrokenCompute(VkDriverId driver_id,
u32 driver_version) {
if (driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
@@ -804,7 +794,6 @@ private:
bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
bool has_broken_compute{}; ///< Compute shaders can cause crashes
bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit
bool has_broken_parallel_compiling{}; ///< Has broken parallel shader compiling.
bool has_renderdoc{}; ///< Has RenderDoc attached
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
bool supports_d24_depth{}; ///< Supports D24 depth buffers.

View File

@@ -252,7 +252,6 @@ file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
if (ENABLE_QT_TRANSLATION)
set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
option(WORKAROUND_BROKEN_LUPDATE "Run lupdate directly through CMake if Qt's convenience wrappers don't work" OFF)
# Update source TS file if enabled
if (GENERATE_QT_TRANSLATION)
@@ -260,51 +259,19 @@ if (ENABLE_QT_TRANSLATION)
# these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals
# so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm
set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
if (WORKAROUND_BROKEN_LUPDATE)
add_custom_command(OUTPUT ${YUZU_QT_LANGUAGES}/en.ts
COMMAND lupdate
-source-language en_US
-target-language en_US
${SRCS}
${UIS}
-ts ${YUZU_QT_LANGUAGES}/en.ts
DEPENDS
${SRCS}
${UIS}
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
)
else()
qt_create_translation(QM_FILES
${SRCS}
${UIS}
${YUZU_QT_LANGUAGES}/en.ts
OPTIONS
-source-language en_US
-target-language en_US
)
endif()
qt_create_translation(QM_FILES
${SRCS}
${UIS}
${YUZU_QT_LANGUAGES}/en.ts
OPTIONS
-source-language en_US
-target-language en_US
)
# Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts
set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts)
set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals")
if (WORKAROUND_BROKEN_LUPDATE)
add_custom_command(OUTPUT ${GENERATED_PLURALS_FILE}
COMMAND lupdate
-source-language en_US
-target-language en_US
${SRCS}
${UIS}
-ts ${GENERATED_PLURALS_FILE}
DEPENDS
${SRCS}
${UIS}
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
)
else()
qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US)
endif()
qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US)
add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE})
endif()

View File

@@ -1,18 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/time_zone.h"
#include "yuzu/configuration/shared_translation.h"
#include <map>
#include <memory>
#include <tuple>
#include <utility>
#include <QCoreApplication>
#include <QWidget>
#include "common/settings.h"
#include "common/settings_enums.h"
#include "common/settings_setting.h"
#include "common/time_zone.h"
#include "yuzu/uisettings.h"
namespace ConfigurationShared {
@@ -22,135 +21,123 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); };
#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \
translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}})
translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{tr((NAME)), tr((TOOLTIP))}})
// A setting can be ignored by giving it a blank name
// Audio
INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral());
INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral());
INSERT(Settings, audio_input_device_id, tr("Input Device:"), QStringLiteral());
INSERT(Settings, audio_muted, tr("Mute audio"), QStringLiteral());
INSERT(Settings, volume, tr("Volume:"), QStringLiteral());
INSERT(Settings, dump_audio_commands, QStringLiteral(), QStringLiteral());
INSERT(UISettings, mute_when_in_background, tr("Mute audio when in background"),
QStringLiteral());
INSERT(Settings, sink_id, "Output Engine:", "");
INSERT(Settings, audio_output_device_id, "Output Device:", "");
INSERT(Settings, audio_input_device_id, "Input Device:", "");
INSERT(Settings, audio_muted, "Mute audio", "");
INSERT(Settings, volume, "Volume:", "");
INSERT(Settings, dump_audio_commands, "", "");
INSERT(UISettings, mute_when_in_background, "Mute audio when in background", "");
// Core
INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral());
INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral());
INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral());
INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral());
INSERT(Settings, use_multi_core, "Multicore CPU Emulation", "");
INSERT(Settings, memory_layout_mode, "Memory Layout", "");
INSERT(Settings, use_speed_limit, "", "");
INSERT(Settings, speed_limit, "Limit Speed Percent", "");
// Cpu
INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral());
INSERT(Settings, cpu_accuracy, "Accuracy:", "");
// Cpu Debug
// Cpu Unsafe
INSERT(Settings, cpuopt_unsafe_unfuse_fma,
"Unfuse FMA (improve performance on CPUs without FMA)",
"This option improves speed by reducing accuracy of fused-multiply-add instructions on "
"CPUs without native FMA support.");
INSERT(Settings, cpuopt_unsafe_reduce_fp_error, "Faster FRSQRTE and FRECPE",
"This option improves the speed of some approximate floating-point functions by using "
"less accurate native approximations.");
INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, "Faster ASIMD instructions (32 bits only)",
"This option improves the speed of 32 bits ASIMD floating-point functions by running "
"with incorrect rounding modes.");
INSERT(Settings, cpuopt_unsafe_inaccurate_nan, "Inaccurate NaN handling",
"This option improves speed by removing NaN checking. Please note this also reduces "
"accuracy of certain floating-point instructions.");
INSERT(
Settings, cpuopt_unsafe_unfuse_fma,
tr("Unfuse FMA (improve performance on CPUs without FMA)"),
tr("This option improves speed by reducing accuracy of fused-multiply-add instructions on "
"CPUs without native FMA support."));
INSERT(
Settings, cpuopt_unsafe_reduce_fp_error, tr("Faster FRSQRTE and FRECPE"),
tr("This option improves the speed of some approximate floating-point functions by using "
"less accurate native approximations."));
INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr,
tr("Faster ASIMD instructions (32 bits only)"),
tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "
"with incorrect rounding modes."));
INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"),
tr("This option improves speed by removing NaN checking. Please note this also reduces "
"accuracy of certain floating-point instructions."));
INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"),
tr("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."));
INSERT(
Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"),
tr("This option improves speed by relying only on the semantics of cmpxchg to ensure "
Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks",
"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.");
INSERT(Settings, cpuopt_unsafe_ignore_global_monitor, "Ignore global monitor",
"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."));
"other race conditions.");
// Renderer
INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral());
INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral());
INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral());
INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral());
INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral());
INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral());
INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral());
INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral());
INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral());
INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral());
INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
QStringLiteral());
INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral());
INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral());
INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), QStringLiteral());
INSERT(
Settings, vsync_mode, tr("VSync Mode:"),
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
INSERT(Settings, renderer_backend, "API:", "");
INSERT(Settings, vulkan_device, "Device:", "");
INSERT(Settings, shader_backend, "Shader Backend:", "");
INSERT(Settings, resolution_setup, "Resolution:", "");
INSERT(Settings, scaling_filter, "Window Adapting Filter:", "");
INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", "");
INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", "");
INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", "");
INSERT(Settings, aspect_ratio, "Aspect Ratio:", "");
INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", "");
INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", "");
INSERT(Settings, nvdec_emulation, "NVDEC emulation:", "");
INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", "");
INSERT(Settings, astc_recompression, "ASTC Recompression Method:", "");
INSERT(Settings, vsync_mode, "VSync Mode:",
"FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
"refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from "
"a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop "
"frames.\nImmediate (no synchronization) just presents whatever is available and can "
"exhibit tearing."));
INSERT(Settings, bg_red, QStringLiteral(), QStringLiteral());
INSERT(Settings, bg_green, QStringLiteral(), QStringLiteral());
INSERT(Settings, bg_blue, QStringLiteral(), QStringLiteral());
"exhibit tearing.");
INSERT(Settings, bg_red, "", "");
INSERT(Settings, bg_green, "", "");
INSERT(Settings, bg_blue, "", "");
// Renderer (Advanced Graphics)
INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"),
QStringLiteral());
INSERT(
Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"),
tr("Runs work in the background while waiting for graphics commands to keep the GPU from "
"lowering its clock speed."));
INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral());
INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral());
INSERT(
Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"),
tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature "
"is experimental."));
INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"),
tr("Enables Fast GPU Time. This option will force most games to run at their highest "
"native resolution."));
INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"),
tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading "
"time significantly in cases where the Vulkan driver does not store pipeline cache "
"files internally."));
INSERT(
Settings, enable_compute_pipelines, tr("Enable Compute Pipelines (Intel Vulkan Only)"),
tr("Enable compute pipelines, required by some games.\nThis setting only exists for Intel "
INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", "");
INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)",
"Runs work in the background while waiting for graphics commands to keep the GPU from "
"lowering its clock speed.");
INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", "");
INSERT(Settings, gpu_accuracy, "Accuracy Level:", "");
INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)",
"Enables asynchronous shader compilation, which may reduce shader stutter. This feature "
"is experimental.");
INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)",
"Enables Fast GPU Time. This option will force most games to run at their highest "
"native resolution.");
INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache",
"Enables GPU vendor-specific pipeline cache. This option can improve shader loading "
"time significantly in cases where the Vulkan driver does not store pipeline cache "
"files internally.");
INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)",
"Enable compute pipelines, required by some games.\nThis setting only exists for Intel "
"proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled "
"on all other drivers."));
INSERT(
Settings, use_reactive_flushing, tr("Enable Reactive Flushing"),
tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory "
"syncing."));
INSERT(Settings, use_video_framerate, tr("Sync to framerate of video playback"),
tr("Run the game at normal speed during video playback, even when the framerate is "
"unlocked."));
INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"),
tr("Improves rendering of transparency effects in specific games."));
"on all other drivers.");
INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing",
"Uses reactive flushing instead of predictive flushing, allowing more accurate memory "
"syncing.");
INSERT(Settings, use_video_framerate, "Sync to framerate of video playback",
"Run the game at normal speed during video playback, even when the framerate is "
"unlocked.");
INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops",
"Improves rendering of transparency effects in specific games.");
// Renderer (Debug)
// System
INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral());
INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral());
INSERT(Settings, device_name, tr("Device Name"), QStringLiteral());
INSERT(Settings, custom_rtc, tr("Custom RTC"), QStringLiteral());
INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());
INSERT(Settings, language_index, tr("Language:"),
tr("Note: this can be overridden when region setting is auto-select"));
INSERT(Settings, region_index, tr("Region:"), QStringLiteral());
INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral());
INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral());
INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral());
INSERT(Settings, current_user, QStringLiteral(), QStringLiteral());
INSERT(Settings, rng_seed, "RNG Seed", "");
INSERT(Settings, rng_seed_enabled, "", "");
INSERT(Settings, device_name, "Device Name", "");
INSERT(Settings, custom_rtc, "Custom RTC", "");
INSERT(Settings, custom_rtc_enabled, "", "");
INSERT(Settings, language_index,
"Language:", "Note: this can be overridden when region setting is auto-select");
INSERT(Settings, region_index, "Region:", "");
INSERT(Settings, time_zone_index, "Time Zone:", "");
INSERT(Settings, sound_index, "Sound Output Mode:", "");
INSERT(Settings, use_docked_mode, "Console Mode:", "");
INSERT(Settings, current_user, "", "");
// Controls
@@ -167,14 +154,11 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
// Ui
// Ui General
INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), QStringLiteral());
INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"),
QStringLiteral());
INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"),
QStringLiteral());
INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral());
INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
QStringLiteral());
INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", "");
INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", "");
INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", "");
INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", "");
INSERT(UISettings, controller_applet_disabled, "Disable controller applet", "");
// Ui Debugging
@@ -194,141 +178,140 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
return parent->tr(text, context);
};
#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast<u32>(Settings::ENUM::VALUE), (TRANSLATION)}
#define PAIR(ENUM, VALUE, TRANSLATION) \
{ static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION) }
#define CTX_PAIR(ENUM, VALUE, TRANSLATION, CONTEXT) \
{ static_cast<u32>(Settings::ENUM::VALUE), tr(TRANSLATION, CONTEXT) }
// Intentionally skipping VSyncMode to let the UI fill that one out
translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
{
PAIR(AstcDecodeMode, Cpu, tr("CPU")),
PAIR(AstcDecodeMode, Gpu, tr("GPU")),
PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")),
PAIR(AstcDecodeMode, Cpu, "CPU"),
PAIR(AstcDecodeMode, Gpu, "GPU"),
PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"),
}});
translations->insert({Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
{
PAIR(AstcRecompression, Uncompressed, "Uncompressed (Best quality)"),
PAIR(AstcRecompression, Bc1, "BC1 (Low quality)"),
PAIR(AstcRecompression, Bc3, "BC3 (Medium quality)"),
}});
translations->insert(
{Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
{
PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")),
PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
}});
translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
{
#ifdef HAS_OPENGL
PAIR(RendererBackend, OpenGL, tr("OpenGL")),
PAIR(RendererBackend, OpenGL, "OpenGL"),
#endif
PAIR(RendererBackend, Vulkan, tr("Vulkan")),
PAIR(RendererBackend, Null, tr("Null")),
PAIR(RendererBackend, Vulkan, "Vulkan"),
PAIR(RendererBackend, Null, "Null"),
}});
translations->insert({Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
{
PAIR(ShaderBackend, Glsl, "GLSL"),
PAIR(ShaderBackend, Glasm, "GLASM (Assembly Shaders, NVIDIA Only)"),
PAIR(ShaderBackend, SpirV, "SPIR-V (Experimental, Mesa Only)"),
}});
translations->insert(
{Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
{
PAIR(ShaderBackend, Glsl, tr("GLSL")),
PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")),
PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, Mesa Only)")),
}});
translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
{
PAIR(GpuAccuracy, Normal, tr("Normal")),
PAIR(GpuAccuracy, High, tr("High")),
PAIR(GpuAccuracy, Extreme, tr("Extreme")),
PAIR(GpuAccuracy, Normal, "Normal"),
PAIR(GpuAccuracy, High, "High"),
PAIR(GpuAccuracy, Extreme, "Extreme"),
}});
translations->insert({Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
{
PAIR(CpuAccuracy, Auto, "Auto"),
PAIR(CpuAccuracy, Accurate, "Accurate"),
PAIR(CpuAccuracy, Unsafe, "Unsafe"),
PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"),
}});
translations->insert(
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
{
PAIR(CpuAccuracy, Auto, tr("Auto")),
PAIR(CpuAccuracy, Accurate, tr("Accurate")),
PAIR(CpuAccuracy, Unsafe, tr("Unsafe")),
PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")),
}});
translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(),
{
PAIR(FullscreenMode, Borderless, tr("Borderless Windowed")),
PAIR(FullscreenMode, Exclusive, tr("Exclusive Fullscreen")),
PAIR(FullscreenMode, Borderless, "Borderless Windowed"),
PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"),
}});
translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(),
{
PAIR(NvdecEmulation, Off, tr("No Video Output")),
PAIR(NvdecEmulation, Cpu, tr("CPU Video Decoding")),
PAIR(NvdecEmulation, Gpu, tr("GPU Video Decoding (Default)")),
PAIR(NvdecEmulation, Off, "No Video Output"),
PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"),
PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"),
}});
translations->insert({Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
{
PAIR(ResolutionSetup, Res1_2X, "0.5X (360p/540p) [EXPERIMENTAL]"),
PAIR(ResolutionSetup, Res3_4X, "0.75X (540p/810p) [EXPERIMENTAL]"),
PAIR(ResolutionSetup, Res1X, "1X (720p/1080p)"),
PAIR(ResolutionSetup, Res3_2X, "1.5X (1080p/1620p) [EXPERIMENTAL]"),
PAIR(ResolutionSetup, Res2X, "2X (1440p/2160p)"),
PAIR(ResolutionSetup, Res3X, "3X (2160p/3240p)"),
PAIR(ResolutionSetup, Res4X, "4X (2880p/4320p)"),
PAIR(ResolutionSetup, Res5X, "5X (3600p/5400p)"),
PAIR(ResolutionSetup, Res6X, "6X (4320p/6480p)"),
PAIR(ResolutionSetup, Res7X, "7X (5040p/7560p)"),
PAIR(ResolutionSetup, Res8X, "8X (5760p/8640p)"),
}});
translations->insert(
{Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
{
PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")),
PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")),
PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")),
PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")),
PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")),
PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")),
PAIR(ResolutionSetup, Res4X, tr("4X (2880p/4320p)")),
PAIR(ResolutionSetup, Res5X, tr("5X (3600p/5400p)")),
PAIR(ResolutionSetup, Res6X, tr("6X (4320p/6480p)")),
PAIR(ResolutionSetup, Res7X, tr("7X (5040p/7560p)")),
PAIR(ResolutionSetup, Res8X, tr("8X (5760p/8640p)")),
}});
translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(),
{
PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")),
PAIR(ScalingFilter, Bilinear, tr("Bilinear")),
PAIR(ScalingFilter, Bicubic, tr("Bicubic")),
PAIR(ScalingFilter, Gaussian, tr("Gaussian")),
PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")),
PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™ Super Resolution")),
PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"),
PAIR(ScalingFilter, Bilinear, "Bilinear"),
PAIR(ScalingFilter, Bicubic, "Bicubic"),
PAIR(ScalingFilter, Gaussian, "Gaussian"),
PAIR(ScalingFilter, ScaleForce, "ScaleForce"),
PAIR(ScalingFilter, Fsr, "AMD FidelityFX™ Super Resolution"),
}});
translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
{
PAIR(AntiAliasing, None, tr("None")),
PAIR(AntiAliasing, Fxaa, tr("FXAA")),
PAIR(AntiAliasing, Smaa, tr("SMAA")),
PAIR(AntiAliasing, None, "None"),
PAIR(AntiAliasing, Fxaa, "FXAA"),
PAIR(AntiAliasing, Smaa, "SMAA"),
}});
translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(),
{
PAIR(AspectRatio, R16_9, tr("Default (16:9)")),
PAIR(AspectRatio, R4_3, tr("Force 4:3")),
PAIR(AspectRatio, R21_9, tr("Force 21:9")),
PAIR(AspectRatio, R16_10, tr("Force 16:10")),
PAIR(AspectRatio, Stretch, tr("Stretch to Window")),
PAIR(AspectRatio, R16_9, "Default (16:9)"),
PAIR(AspectRatio, R4_3, "Force 4:3"),
PAIR(AspectRatio, R21_9, "Force 21:9"),
PAIR(AspectRatio, R16_10, "Force 16:10"),
PAIR(AspectRatio, Stretch, "Stretch to Window"),
}});
translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(),
{
PAIR(AnisotropyMode, Automatic, tr("Automatic")),
PAIR(AnisotropyMode, Default, tr("Default")),
PAIR(AnisotropyMode, X2, tr("2x")),
PAIR(AnisotropyMode, X4, tr("4x")),
PAIR(AnisotropyMode, X8, tr("8x")),
PAIR(AnisotropyMode, X16, tr("16x")),
PAIR(AnisotropyMode, Automatic, "Automatic"),
PAIR(AnisotropyMode, Default, "Default"),
PAIR(AnisotropyMode, X2, "2x"),
PAIR(AnisotropyMode, X4, "4x"),
PAIR(AnisotropyMode, X8, "8x"),
PAIR(AnisotropyMode, X16, "16x"),
}});
translations->insert(
{Settings::EnumMetadata<Settings::Language>::Index(),
{
PAIR(Language, Japanese, tr("Japanese (日本語)")),
PAIR(Language, EnglishAmerican, tr("American English")),
PAIR(Language, French, tr("French (français)")),
PAIR(Language, German, tr("German (Deutsch)")),
PAIR(Language, Italian, tr("Italian (italiano)")),
PAIR(Language, Spanish, tr("Spanish (español)")),
PAIR(Language, Chinese, tr("Chinese")),
PAIR(Language, Korean, tr("Korean (한국어)")),
PAIR(Language, Dutch, tr("Dutch (Nederlands)")),
PAIR(Language, Portuguese, tr("Portuguese (português)")),
PAIR(Language, Russian, tr("Russian (Русский)")),
PAIR(Language, Taiwanese, tr("Taiwanese")),
PAIR(Language, EnglishBritish, tr("British English")),
PAIR(Language, FrenchCanadian, tr("Canadian French")),
PAIR(Language, SpanishLatin, tr("Latin American Spanish")),
PAIR(Language, ChineseSimplified, tr("Simplified Chinese")),
PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")),
PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")),
PAIR(Language, Japanese, "Japanese (日本語)"),
PAIR(Language, EnglishAmerican, "American English"),
PAIR(Language, French, "French (français)"),
PAIR(Language, German, "German (Deutsch)"),
PAIR(Language, Italian, "Italian (italiano)"),
PAIR(Language, Spanish, "Spanish (español)"),
PAIR(Language, Chinese, "Chinese"),
PAIR(Language, Korean, "Korean (한국어)"),
PAIR(Language, Dutch, "Dutch (Nederlands)"),
PAIR(Language, Portuguese, "Portuguese (português)"),
PAIR(Language, Russian, "Russian (Русский)"),
PAIR(Language, Taiwanese, "Taiwanese"),
PAIR(Language, EnglishBritish, "British English"),
PAIR(Language, FrenchCanadian, "Canadian French"),
PAIR(Language, SpanishLatin, "Latin American Spanish"),
PAIR(Language, ChineseSimplified, "Simplified Chinese"),
PAIR(Language, ChineseTraditional, "Traditional Chinese (正體中文)"),
PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"),
}});
translations->insert({Settings::EnumMetadata<Settings::Region>::Index(),
{
PAIR(Region, Japan, tr("Japan")),
PAIR(Region, Usa, tr("USA")),
PAIR(Region, Europe, tr("Europe")),
PAIR(Region, Australia, tr("Australia")),
PAIR(Region, China, tr("China")),
PAIR(Region, Korea, tr("Korea")),
PAIR(Region, Taiwan, tr("Taiwan")),
PAIR(Region, Japan, "Japan"),
PAIR(Region, Usa, "USA"),
PAIR(Region, Europe, "Europe"),
PAIR(Region, Australia, "Australia"),
PAIR(Region, China, "China"),
PAIR(Region, Korea, "Korea"),
PAIR(Region, Taiwan, "Taiwan"),
}});
translations->insert(
{Settings::EnumMetadata<Settings::TimeZone>::Index(),
@@ -340,74 +323,72 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
{static_cast<u32>(Settings::TimeZone::Default),
tr("Default (%1)", "Default time zone")
.arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))},
PAIR(TimeZone, Cet, tr("CET")),
PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")),
PAIR(TimeZone, Cuba, tr("Cuba")),
PAIR(TimeZone, Eet, tr("EET")),
PAIR(TimeZone, Egypt, tr("Egypt")),
PAIR(TimeZone, Eire, tr("Eire")),
PAIR(TimeZone, Est, tr("EST")),
PAIR(TimeZone, Est5Edt, tr("EST5EDT")),
PAIR(TimeZone, Gb, tr("GB")),
PAIR(TimeZone, GbEire, tr("GB-Eire")),
PAIR(TimeZone, Gmt, tr("GMT")),
PAIR(TimeZone, GmtPlusZero, tr("GMT+0")),
PAIR(TimeZone, GmtMinusZero, tr("GMT-0")),
PAIR(TimeZone, GmtZero, tr("GMT0")),
PAIR(TimeZone, Greenwich, tr("Greenwich")),
PAIR(TimeZone, Hongkong, tr("Hongkong")),
PAIR(TimeZone, Hst, tr("HST")),
PAIR(TimeZone, Iceland, tr("Iceland")),
PAIR(TimeZone, Iran, tr("Iran")),
PAIR(TimeZone, Israel, tr("Israel")),
PAIR(TimeZone, Jamaica, tr("Jamaica")),
PAIR(TimeZone, Japan, tr("Japan")),
PAIR(TimeZone, Kwajalein, tr("Kwajalein")),
PAIR(TimeZone, Libya, tr("Libya")),
PAIR(TimeZone, Met, tr("MET")),
PAIR(TimeZone, Mst, tr("MST")),
PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")),
PAIR(TimeZone, Navajo, tr("Navajo")),
PAIR(TimeZone, Nz, tr("NZ")),
PAIR(TimeZone, NzChat, tr("NZ-CHAT")),
PAIR(TimeZone, Poland, tr("Poland")),
PAIR(TimeZone, Portugal, tr("Portugal")),
PAIR(TimeZone, Prc, tr("PRC")),
PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")),
PAIR(TimeZone, Roc, tr("ROC")),
PAIR(TimeZone, Rok, tr("ROK")),
PAIR(TimeZone, Singapore, tr("Singapore")),
PAIR(TimeZone, Turkey, tr("Turkey")),
PAIR(TimeZone, Uct, tr("UCT")),
PAIR(TimeZone, Universal, tr("Universal")),
PAIR(TimeZone, Utc, tr("UTC")),
PAIR(TimeZone, WSu, tr("W-SU")),
PAIR(TimeZone, Wet, tr("WET")),
PAIR(TimeZone, Zulu, tr("Zulu")),
PAIR(TimeZone, Cet, "CET"),
PAIR(TimeZone, Cst6Cdt, "CST6CDT"),
PAIR(TimeZone, Cuba, "Cuba"),
PAIR(TimeZone, Eet, "EET"),
PAIR(TimeZone, Egypt, "Egypt"),
PAIR(TimeZone, Eire, "Eire"),
PAIR(TimeZone, Est, "EST"),
PAIR(TimeZone, Est5Edt, "EST5EDT"),
PAIR(TimeZone, Gb, "GB"),
PAIR(TimeZone, GbEire, "GB-Eire"),
PAIR(TimeZone, Gmt, "GMT"),
PAIR(TimeZone, GmtPlusZero, "GMT+0"),
PAIR(TimeZone, GmtMinusZero, "GMT-0"),
PAIR(TimeZone, GmtZero, "GMT0"),
PAIR(TimeZone, Greenwich, "Greenwich"),
PAIR(TimeZone, Hongkong, "Hongkong"),
PAIR(TimeZone, Hst, "HST"),
PAIR(TimeZone, Iceland, "Iceland"),
PAIR(TimeZone, Iran, "Iran"),
PAIR(TimeZone, Israel, "Israel"),
PAIR(TimeZone, Jamaica, "Jamaica"),
PAIR(TimeZone, Japan, "Japan"),
PAIR(TimeZone, Kwajalein, "Kwajalein"),
PAIR(TimeZone, Libya, "Libya"),
PAIR(TimeZone, Met, "MET"),
PAIR(TimeZone, Mst, "MST"),
PAIR(TimeZone, Mst7Mdt, "MST7MDT"),
PAIR(TimeZone, Navajo, "Navajo"),
PAIR(TimeZone, Nz, "NZ"),
PAIR(TimeZone, NzChat, "NZ-CHAT"),
PAIR(TimeZone, Poland, "Poland"),
PAIR(TimeZone, Portugal, "Portugal"),
PAIR(TimeZone, Prc, "PRC"),
PAIR(TimeZone, Pst8Pdt, "PST8PDT"),
PAIR(TimeZone, Roc, "ROC"),
PAIR(TimeZone, Rok, "ROK"),
PAIR(TimeZone, Singapore, "Singapore"),
PAIR(TimeZone, Turkey, "Turkey"),
PAIR(TimeZone, Uct, "UCT"),
PAIR(TimeZone, Universal, "Universal"),
PAIR(TimeZone, Utc, "UTC"),
PAIR(TimeZone, WSu, "W-SU"),
PAIR(TimeZone, Wet, "WET"),
PAIR(TimeZone, Zulu, "Zulu"),
}});
translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(),
{
PAIR(AudioMode, Mono, tr("Mono")),
PAIR(AudioMode, Stereo, tr("Stereo")),
PAIR(AudioMode, Surround, tr("Surround")),
PAIR(AudioMode, Mono, "Mono"),
PAIR(AudioMode, Stereo, "Stereo"),
PAIR(AudioMode, Surround, "Surround"),
}});
translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(),
{
PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")),
PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")),
PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM (Unsafe)")),
}});
translations->insert({Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
{
PAIR(ConsoleMode, Docked, tr("Docked")),
PAIR(ConsoleMode, Handheld, tr("Handheld")),
PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"),
PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"),
PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"),
}});
translations->insert(
{Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
{PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
translations->insert(
{Settings::EnumMetadata<Settings::ConfirmStop>::Index(),
{
PAIR(ConfirmStop, Ask_Always, tr("Always ask (Default)")),
PAIR(ConfirmStop, Ask_Based_On_Game, tr("Only if game specifies not to stop")),
PAIR(ConfirmStop, Ask_Never, tr("Never ask")),
PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"),
PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"),
PAIR(ConfirmStop, Ask_Never, "Never ask"),
}});
#undef PAIR

View File

@@ -194,7 +194,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
return group;
}
const auto get_selected = [this]() -> int {
const auto get_selected = [=]() -> int {
for (const auto& [id, button] : radio_buttons) {
if (button->isChecked()) {
return id;
@@ -203,7 +203,7 @@ QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
return -1;
};
const auto set_index = [this](u32 value) {
const auto set_index = [=](u32 value) {
for (const auto& [id, button] : radio_buttons) {
button->setChecked(id == value);
}

View File

@@ -567,10 +567,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
// TODO: Implement shortcut creation for macOS
#if !defined(__APPLE__)
QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut"));
QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop"));
#ifndef WIN32
QAction* create_applications_menu_shortcut =
shortcut_menu->addAction(tr("Add to Applications Menu"));
#endif
@@ -648,11 +647,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {
emit NavigateToGamedbEntryRequested(program_id, compatibility_list);
});
// TODO: Implement shortcut creation for macOS
#if !defined(__APPLE__)
connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() {
emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop);
});
#ifndef WIN32
connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() {
emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications);
});

View File

@@ -479,6 +479,6 @@ void GameListWorker::run() {
}
}
RecordEvent([this](GameList* game_list) { game_list->DonePopulating(watch_list); });
RecordEvent([=](GameList* game_list) { game_list->DonePopulating(watch_list); });
processing_completed.Set();
}

View File

@@ -10,7 +10,6 @@
#include <thread>
#include "core/loader/nca.h"
#include "core/tools/renderdoc.h"
#ifdef __APPLE__
#include <unistd.h> // for chdir
#endif
@@ -1575,7 +1574,6 @@ void GMainWindow::ConnectMenuEvents() {
connect_menu(ui->action_Load_Cabinet_Formatter,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); });
connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit);
connect_menu(ui->action_Open_Controller_Menu, &GMainWindow::OnOpenControllerMenu);
connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot);
// TAS
@@ -1603,13 +1601,14 @@ void GMainWindow::UpdateMenuState() {
ui->action_Pause,
};
const std::array applet_actions{ui->action_Load_Album,
ui->action_Load_Cabinet_Nickname_Owner,
ui->action_Load_Cabinet_Eraser,
ui->action_Load_Cabinet_Restorer,
ui->action_Load_Cabinet_Formatter,
ui->action_Load_Mii_Edit,
ui->action_Open_Controller_Menu};
const std::array applet_actions{
ui->action_Load_Album,
ui->action_Load_Cabinet_Nickname_Owner,
ui->action_Load_Cabinet_Eraser,
ui->action_Load_Cabinet_Restorer,
ui->action_Load_Cabinet_Formatter,
ui->action_Load_Mii_Edit,
};
for (QAction* action : running_actions) {
action->setEnabled(emulation_running);
@@ -2848,259 +2847,170 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory));
}
bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
const std::string& comment,
const std::filesystem::path& icon_path,
const std::filesystem::path& command,
const std::string& arguments, const std::string& categories,
const std::string& keywords, const std::string& name) try {
#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD
std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
if (!shortcut_stream.is_open()) {
LOG_ERROR(Frontend, "Failed to create shortcut");
return false;
}
// TODO: Migrate fmt::print to std::print in futures STD C++ 23.
fmt::print(shortcut_stream, "[Desktop Entry]\n");
fmt::print(shortcut_stream, "Type=Application\n");
fmt::print(shortcut_stream, "Version=1.0\n");
fmt::print(shortcut_stream, "Name={}\n", name);
if (!comment.empty()) {
fmt::print(shortcut_stream, "Comment={}\n", comment);
}
if (std::filesystem::is_regular_file(icon_path)) {
fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
}
fmt::print(shortcut_stream, "TryExec={}\n", command.string());
fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
if (!categories.empty()) {
fmt::print(shortcut_stream, "Categories={}\n", categories);
}
if (!keywords.empty()) {
fmt::print(shortcut_stream, "Keywords={}\n", keywords);
}
return true;
#elif defined(_WIN32) // Windows
HRESULT hr = CoInitialize(nullptr);
if (FAILED(hr)) {
LOG_ERROR(Frontend, "CoInitialize failed");
return false;
}
SCOPE_EXIT({ CoUninitialize(); });
IShellLinkW* ps1 = nullptr;
IPersistFile* persist_file = nullptr;
SCOPE_EXIT({
if (persist_file != nullptr) {
persist_file->Release();
}
if (ps1 != nullptr) {
ps1->Release();
}
});
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
reinterpret_cast<void**>(&ps1));
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to create IShellLinkW instance");
return false;
}
hres = ps1->SetPath(command.c_str());
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set path");
return false;
}
if (!arguments.empty()) {
hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data());
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set arguments");
return false;
}
}
if (!comment.empty()) {
hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data());
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set description");
return false;
}
}
if (std::filesystem::is_regular_file(icon_path)) {
hres = ps1->SetIconLocation(icon_path.c_str(), 0);
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set icon location");
return false;
}
}
hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&persist_file));
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to get IPersistFile interface");
return false;
}
hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE);
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to save shortcut");
return false;
}
return true;
#else // Unsupported platform
return false;
#endif
} catch (const std::exception& e) {
LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what());
return false;
}
// Messages in pre-defined message boxes for less code spaghetti
bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) {
int result = 0;
QMessageBox::StandardButtons buttons;
switch (imsg) {
case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES:
buttons = QMessageBox::Yes | QMessageBox::No;
result =
QMessageBox::information(parent, tr("Create Shortcut"),
tr("Do you want to launch the game in fullscreen?"), buttons);
return result == QMessageBox::Yes;
case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS:
QMessageBox::information(parent, tr("Create Shortcut"),
tr("Successfully created a shortcut to %1").arg(game_title));
return false;
case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING:
buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel;
result =
QMessageBox::warning(this, tr("Create Shortcut"),
tr("This will create a shortcut to the current AppImage. This may "
"not work well if you update. Continue?"),
buttons);
return result == QMessageBox::Ok;
default:
buttons = QMessageBox::Ok;
QMessageBox::critical(parent, tr("Create Shortcut"),
tr("Failed to create a shortcut to %1").arg(game_title), buttons);
return false;
}
}
bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
std::filesystem::path& out_icon_path) {
// Get path to Yuzu icons directory & icon extension
std::string ico_extension = "png";
#if defined(_WIN32)
out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir);
ico_extension = "ico";
#elif defined(__linux__) || defined(__FreeBSD__)
out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
#endif
// Create icons directory if it doesn't exist
if (!Common::FS::CreateDirs(out_icon_path)) {
QMessageBox::critical(
this, tr("Create Icon"),
tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
.arg(QString::fromStdString(out_icon_path.string())),
QMessageBox::StandardButton::Ok);
out_icon_path.clear();
return false;
}
// Create icon file path
out_icon_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension)
: fmt::format("yuzu-{:016X}.{}", program_id, ico_extension));
return true;
}
void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
GameListShortcutTarget target) {
std::string game_title;
QString qt_game_title;
std::filesystem::path out_icon_path;
// Get path to yuzu executable
const QStringList args = QApplication::arguments();
std::filesystem::path yuzu_command = args[0].toStdString();
// If relative path, make it an absolute path
if (yuzu_command.c_str()[0] == '.') {
yuzu_command = Common::FS::GetCurrentDir() / yuzu_command;
}
// Shortcut path
std::filesystem::path shortcut_path{};
if (target == GameListShortcutTarget::Desktop) {
shortcut_path =
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
} else if (target == GameListShortcutTarget::Applications) {
shortcut_path =
QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
}
// Icon path and title
if (std::filesystem::exists(shortcut_path)) {
// Get title from game file
const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
system->GetContentProvider()};
const auto control = pm.GetControlMetadata();
const auto loader =
Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
game_title = fmt::format("{:016X}", program_id);
if (control.first != nullptr) {
game_title = control.first->GetApplicationName();
} else {
loader->ReadTitle(game_title);
}
// Delete illegal characters from title
const std::string illegal_chars = "<>:\"/\\|?*.";
for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
if (illegal_chars.find(*it) != std::string::npos) {
game_title.erase(it.base() - 1);
}
}
qt_game_title = QString::fromStdString(game_title);
// Get icon from game file
std::vector<u8> icon_image_file{};
if (control.second != nullptr) {
icon_image_file = control.second->ReadAllBytes();
} else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
}
QImage icon_data =
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
if (!SaveIconToFile(out_icon_path, icon_data)) {
LOG_ERROR(Frontend, "Could not write icon to file");
}
}
} else {
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
qt_game_title);
LOG_ERROR(Frontend, "Invalid shortcut target");
return;
}
#if defined(__linux__)
// Special case for AppImages
// Warn once if we are making a shortcut to a volatile AppImage
const std::string appimage_ending =
std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage");
if (yuzu_command.string().ends_with(appimage_ending) &&
!UISettings::values.shortcut_already_warned) {
if (GMainWindow::CreateShortcutMessagesGUI(
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qt_game_title)) {
if (QMessageBox::warning(this, tr("Create Shortcut"),
tr("This will create a shortcut to the current AppImage. This may "
"not work well if you update. Continue?"),
QMessageBox::StandardButton::Ok |
QMessageBox::StandardButton::Cancel) ==
QMessageBox::StandardButton::Cancel) {
return;
}
UISettings::values.shortcut_already_warned = true;
}
#endif // __linux__
// Create shortcut
std::string arguments = fmt::format("-g \"{:s}\"", game_path);
if (GMainWindow::CreateShortcutMessagesGUI(
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) {
arguments = "-f " + arguments;
std::filesystem::path target_directory{};
switch (target) {
case GameListShortcutTarget::Desktop: {
const QString desktop_path =
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
target_directory = desktop_path.toUtf8().toStdString();
break;
}
const std::string comment = fmt::format("Start {:s} with the yuzu Emulator", game_title);
case GameListShortcutTarget::Applications: {
const QString applications_path =
QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
if (applications_path.isEmpty()) {
const char* home = std::getenv("HOME");
if (home != nullptr) {
target_directory = std::filesystem::path(home) / ".local/share/applications";
}
} else {
target_directory = applications_path.toUtf8().toStdString();
}
break;
}
default:
return;
}
const QDir dir(QString::fromStdString(target_directory.generic_string()));
if (!dir.exists()) {
QMessageBox::critical(this, tr("Create Shortcut"),
tr("Cannot create shortcut. Path \"%1\" does not exist.")
.arg(QString::fromStdString(target_directory.generic_string())),
QMessageBox::StandardButton::Ok);
return;
}
const std::string game_file_name = std::filesystem::path(game_path).filename().string();
// Determine full paths for icon and shortcut
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
const char* home = std::getenv("HOME");
const std::filesystem::path home_path = (home == nullptr ? "~" : home);
const char* xdg_data_home = std::getenv("XDG_DATA_HOME");
std::filesystem::path system_icons_path =
(xdg_data_home == nullptr ? home_path / ".local/share/"
: std::filesystem::path(xdg_data_home)) /
"icons/hicolor/256x256";
if (!Common::FS::CreateDirs(system_icons_path)) {
QMessageBox::critical(
this, tr("Create Icon"),
tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
.arg(QString::fromStdString(system_icons_path)),
QMessageBox::StandardButton::Ok);
return;
}
std::filesystem::path icon_path =
system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name)
: fmt::format("yuzu-{:016X}.png", program_id));
const std::filesystem::path shortcut_path =
target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name)
: fmt::format("yuzu-{:016X}.desktop", program_id));
#elif defined(WIN32)
std::filesystem::path icons_path =
Common::FS::GetYuzuPathString(Common::FS::YuzuPath::IconsDir);
std::filesystem::path icon_path =
icons_path / ((program_id == 0 ? fmt::format("yuzu-{}.ico", game_file_name)
: fmt::format("yuzu-{:016X}.ico", program_id)));
#else
std::string icon_extension;
#endif
// Get title from game file
const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
system->GetContentProvider()};
const auto control = pm.GetControlMetadata();
const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
std::string title{fmt::format("{:016X}", program_id)};
if (control.first != nullptr) {
title = control.first->GetApplicationName();
} else {
loader->ReadTitle(title);
}
// Get icon from game file
std::vector<u8> icon_image_file{};
if (control.second != nullptr) {
icon_image_file = control.second->ReadAllBytes();
} else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
}
QImage icon_data =
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
// Convert and write the icon as a PNG
if (!icon_data.save(QString::fromStdString(icon_path.string()))) {
LOG_ERROR(Frontend, "Could not write icon as PNG to file");
} else {
LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
}
#elif defined(WIN32)
if (!SaveIconToFile(icon_path.string(), icon_data)) {
LOG_ERROR(Frontend, "Could not write icon to file");
return;
}
#endif // __linux__
#ifdef _WIN32
// Replace characters that are illegal in Windows filenames by a dash
const std::string illegal_chars = "<>:\"/\\|?*";
for (char c : illegal_chars) {
std::replace(title.begin(), title.end(), c, '_');
}
const std::filesystem::path shortcut_path = target_directory / (title + ".lnk").c_str();
#endif
const std::string comment =
tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString();
const std::string arguments = fmt::format("-g \"{:s}\"", game_path);
const std::string categories = "Game;Emulator;Qt;";
const std::string keywords = "Switch;Nintendo;";
if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, yuzu_command,
arguments, categories, keywords, game_title)) {
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
qt_game_title);
if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(),
yuzu_command.string(), arguments, categories, keywords)) {
QMessageBox::critical(this, tr("Create Shortcut"),
tr("Failed to create a shortcut at %1")
.arg(QString::fromStdString(shortcut_path.string())));
return;
}
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
qt_game_title);
LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string());
QMessageBox::information(
this, tr("Create Shortcut"),
tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title)));
}
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
@@ -4095,6 +4005,66 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
}
}
bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title,
const std::string& comment, const std::string& icon_path,
const std::string& command, const std::string& arguments,
const std::string& categories, const std::string& keywords) {
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
// This desktop file template was writing referencing
// https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html
std::string shortcut_contents{};
shortcut_contents.append("[Desktop Entry]\n");
shortcut_contents.append("Type=Application\n");
shortcut_contents.append("Version=1.0\n");
shortcut_contents.append(fmt::format("Name={:s}\n", title));
shortcut_contents.append(fmt::format("Comment={:s}\n", comment));
shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path));
shortcut_contents.append(fmt::format("TryExec={:s}\n", command));
shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments));
shortcut_contents.append(fmt::format("Categories={:s}\n", categories));
shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords));
std::ofstream shortcut_stream(shortcut_path);
if (!shortcut_stream.is_open()) {
LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path);
return false;
}
shortcut_stream << shortcut_contents;
shortcut_stream.close();
return true;
#elif defined(WIN32)
IShellLinkW* shell_link;
auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
(void**)&shell_link);
if (FAILED(hres)) {
return false;
}
shell_link->SetPath(
Common::UTF8ToUTF16W(command).data()); // Path to the object we are referring to
shell_link->SetArguments(Common::UTF8ToUTF16W(arguments).data());
shell_link->SetDescription(Common::UTF8ToUTF16W(comment).data());
shell_link->SetIconLocation(Common::UTF8ToUTF16W(icon_path).data(), 0);
IPersistFile* persist_file;
hres = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file);
if (FAILED(hres)) {
return false;
}
hres = persist_file->Save(Common::UTF8ToUTF16W(shortcut_path).data(), TRUE);
if (FAILED(hres)) {
return false;
}
persist_file->Release();
shell_link->Release();
return true;
#endif
return false;
}
void GMainWindow::OnLoadAmiibo() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
return;
@@ -4133,6 +4103,7 @@ void GMainWindow::OnLoadAmiibo() {
bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton) {
QMessageBox* box_dialog = new QMessageBox(parent);
box_dialog->setWindowTitle(title);
box_dialog->setText(text);
@@ -4375,31 +4346,6 @@ void GMainWindow::OnMiiEdit() {
BootGame(filename, MiiEditId);
}
void GMainWindow::OnOpenControllerMenu() {
constexpr u64 ControllerAppletId =
static_cast<u64>(Service::AM::Applets::AppletProgramId::Controller);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Controller Menu."));
return;
}
auto controller_applet_nca =
bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program);
if (!controller_applet_nca) {
QMessageBox::warning(this, tr("Controller Applet"),
tr("Controller Menu is not available. Please reinstall firmware."));
return;
}
system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller);
const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path();
BootGame(filename, ControllerAppletId);
}
void GMainWindow::OnCaptureScreenshot() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
return;

View File

@@ -6,7 +6,6 @@
#include <memory>
#include <optional>
#include <filesystem>
#include <QMainWindow>
#include <QMessageBox>
#include <QPushButton>
@@ -175,13 +174,6 @@ class GMainWindow : public QMainWindow {
UI_EMU_STOPPING,
};
enum {
CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
CREATE_SHORTCUT_MSGBOX_SUCCESS,
CREATE_SHORTCUT_MSGBOX_ERROR,
CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING,
};
public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
@@ -410,7 +402,6 @@ private slots:
void OnAlbum();
void OnCabinet(Service::NFP::CabinetMode mode);
void OnMiiEdit();
void OnOpenControllerMenu();
void OnCaptureScreenshot();
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
void OnLanguageChanged(const QString& locale);
@@ -465,14 +456,11 @@ private:
bool ConfirmShutdownGame();
QString GetTasStateDescription() const;
bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title);
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
std::filesystem::path& out_icon_path);
bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment,
const std::filesystem::path& icon_path,
const std::filesystem::path& command, const std::string& arguments,
const std::string& categories, const std::string& keywords,
const std::string& name);
bool CreateShortcut(const std::string& shortcut_path, const std::string& title,
const std::string& comment, const std::string& icon_path,
const std::string& command, const std::string& arguments,
const std::string& categories, const std::string& keywords);
/**
* Mimic the behavior of QMessageBox::question but link controller navigation to the dialog
* The only difference is that it returns a boolean.

View File

@@ -25,7 +25,7 @@
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin" stdset="0">
<property name="margin">
<number>0</number>
</property>
</layout>
@@ -36,7 +36,7 @@
<x>0</x>
<y>0</y>
<width>1280</width>
<height>21</height>
<height>26</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
@@ -162,7 +162,6 @@
<addaction name="menu_cabinet_applet"/>
<addaction name="action_Load_Album"/>
<addaction name="action_Load_Mii_Edit"/>
<addaction name="action_Open_Controller_Menu"/>
<addaction name="separator"/>
<addaction name="action_Capture_Screenshot"/>
<addaction name="menuTAS"/>
@@ -383,9 +382,9 @@
</property>
</action>
<action name="action_Load_Album">
<property name="text">
<string>Open &amp;Album</string>
</property>
<property name="text">
<string>Open &amp;Album</string>
</property>
</action>
<action name="action_Load_Cabinet_Nickname_Owner">
<property name="text">
@@ -408,9 +407,9 @@
</property>
</action>
<action name="action_Load_Mii_Edit">
<property name="text">
<string>Open &amp;Mii Editor</string>
</property>
<property name="text">
<string>Open &amp;Mii Editor</string>
</property>
</action>
<action name="action_Configure_Tas">
<property name="text">
@@ -455,11 +454,6 @@
<string>R&amp;ecord</string>
</property>
</action>
<action name="action_Open_Controller_Menu">
<property name="text">
<string>Open &amp;Controller Menu</string>
</property>
</action>
</widget>
<resources>
<include location="yuzu.qrc"/>

View File

@@ -4,10 +4,7 @@
#include <array>
#include <cmath>
#include <QPainter>
#include "common/logging/log.h"
#include "yuzu/util/util.h"
#ifdef _WIN32
#include <windows.h>
#include "common/fs/file.h"
@@ -45,7 +42,7 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) {
return circle_pixmap;
}
bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) {
bool SaveIconToFile(const std::string_view path, const QImage& image) {
#if defined(WIN32)
#pragma pack(push, 2)
struct IconDir {
@@ -76,7 +73,7 @@ bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image)
.id_count = static_cast<WORD>(scale_sizes.size()),
};
Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write,
Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write,
Common::FS::FileType::BinaryFile);
if (!icon_file.IsOpen()) {
return false;
@@ -137,14 +134,6 @@ bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image)
}
icon_file.Close();
return true;
#elif defined(__linux__) || defined(__FreeBSD__)
// Convert and write the icon as a PNG
if (!image.save(QString::fromStdString(icon_path.string()))) {
LOG_ERROR(Frontend, "Could not write icon as PNG to file");
} else {
LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
}
return true;
#else
return false;

View File

@@ -3,7 +3,6 @@
#pragma once
#include <filesystem>
#include <QFont>
#include <QString>
@@ -26,4 +25,4 @@
* @param image The image to save
* @return bool If the operation succeeded
*/
[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);
[[nodiscard]] bool SaveIconToFile(const std::string_view path, const QImage& image);