mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-26 03:01:17 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e7e8bd7b1a | |||
| 07c6eeaabd | |||
| 29293295d4 | |||
| 2a37ed8e15 | |||
| 9ba0d4a541 | |||
| 150c2a53a8 | |||
| 85eb750d2e | |||
| bebc19da32 | |||
| 99bf8cf51a | |||
| dbeb73ee01 | |||
| cb73a4dcc7 | |||
| 278f411dad | |||
| 3d37a816c6 | |||
| a25e32676f | |||
| 38df54edfe |
+33
-21
@@ -1,32 +1,44 @@
|
|||||||
# User Handbook - Command Line
|
# User Handbook - Command Line
|
||||||
|
|
||||||
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments.
|
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept the same command line arguments.
|
||||||
|
|
||||||
## eden
|
|
||||||
|
|
||||||
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application.
|
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application.
|
||||||
- `-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
|
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
|
||||||
- `-f`: Use fullscreen.
|
- `--config/-c`: Specify alternate configuration file.
|
||||||
- `-u <number>`: Select the index of the user to load as.
|
- `--fullscreen/-f`: Use fullscreen.
|
||||||
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
|
- `--help/-h`: Display help.
|
||||||
|
- `--game/-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
|
||||||
|
- `--multiplayer/-m`: Specify multiplayer options.
|
||||||
|
- `--program/-p`: Specify the program arguments to pass (optional).
|
||||||
|
- `--user/-u <number>`: Specify the user index.
|
||||||
|
- `--version/-v`: Display version and quit.
|
||||||
|
- `--input-profile/-i <name>`: Specifies input profile name to use (for player #0 only).
|
||||||
|
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
|
||||||
|
- `--filter/-x`: Sets the debug log filter irrespective of settings.
|
||||||
|
- `--singlecore/-s`: Forces single-core regardless of settings.
|
||||||
|
|
||||||
|
Only the Qt frontend supports the following arguments:
|
||||||
|
|
||||||
- `-qlaunch`: Launch QLaunch.
|
- `-qlaunch`: Launch QLaunch.
|
||||||
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
|
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
|
||||||
- Requires a copy of Atmosphere to be extracted onto `sdmc`.
|
- Requires a copy of Atmosphere to be extracted onto `sdmc`.
|
||||||
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
|
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
|
||||||
- `-setup`: Launch setup applet.
|
- `-setup`: Launch setup applet.
|
||||||
|
|
||||||
## eden-cli
|
`eden` (provided with `--room`), and `eden-room` supports the following options as well:
|
||||||
|
|
||||||
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
|
- `-n/--room-name`: The name of the room.
|
||||||
- `--config/-c`: Specify alternate configuration file.
|
- `-d/--room-description`: The room description.
|
||||||
- `--fullscreen/-f`: Set fullscreen.
|
- `-s/--bind-address`: The bind address for the room.
|
||||||
- `--help/-h`: Display help.
|
- `-p/--port`: The port used for the room.
|
||||||
- `--game/-g`: Specify the game to run.
|
- `-m/--max-members`: The maximum number of players for this room.
|
||||||
- `--multiplayer/-m`: Specify multiplayer options.
|
- `-w/--password`: The password for the room.
|
||||||
- `--program/-p`: Specify the program arguments to pass (optional).
|
- `-g/--preferred-game`: The preferred game for this room.
|
||||||
- `--user/-u`: Specify the user index.
|
- `-i/--preferred-game-id`: The preferred game-id for this room.
|
||||||
- `--version/-v`: Display version and quit.
|
- `-u/--username`: The username used for announce.
|
||||||
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only).
|
- `-t/--token`: The token used for announce.
|
||||||
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
|
- `-a/--web-api-url`: yuzu Web API url.
|
||||||
- `--filter/-x`: Sets the debug log filter irrespective of settings.
|
- `-b/--ban-list-file`: The file for storing the room ban list.
|
||||||
- `--singlecore/-s`: Forces single-core regardless of settings.
|
- `-l/--log-file`: The file for storing the room log.
|
||||||
|
- `-h/--help`: Display this help and exit.
|
||||||
|
- `-v/--version`: Output version information and exit.
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ add_library(
|
|||||||
logging.h
|
logging.h
|
||||||
lz4_compression.cpp
|
lz4_compression.cpp
|
||||||
lz4_compression.h
|
lz4_compression.h
|
||||||
make_unique_for_overwrite.h
|
|
||||||
math_util.h
|
math_util.h
|
||||||
memory_detect.cpp
|
memory_detect.cpp
|
||||||
memory_detect.h
|
memory_detect.h
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
requires(!std::is_array_v<T>)
|
|
||||||
std::unique_ptr<T> make_unique_for_overwrite() {
|
|
||||||
return std::unique_ptr<T>(new T);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
requires std::is_unbounded_array_v<T>
|
|
||||||
std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
|
|
||||||
return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, class... Args>
|
|
||||||
requires std::is_bounded_array_v<T>
|
|
||||||
void make_unique_for_overwrite(Args&&...) = delete;
|
|
||||||
|
|
||||||
} // namespace Common
|
|
||||||
@@ -8,8 +8,7 @@
|
|||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
#include "common/make_unique_for_overwrite.h"
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
@@ -38,8 +37,9 @@ public:
|
|||||||
ScratchBuffer() = default;
|
ScratchBuffer() = default;
|
||||||
|
|
||||||
explicit ScratchBuffer(size_type initial_capacity)
|
explicit ScratchBuffer(size_type initial_capacity)
|
||||||
: last_requested_size{initial_capacity}, buffer_capacity{initial_capacity},
|
: last_requested_size{initial_capacity}
|
||||||
buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
|
, buffer_capacity{initial_capacity}
|
||||||
|
, buffer{std::make_unique_for_overwrite<T[]>(initial_capacity)} {}
|
||||||
|
|
||||||
~ScratchBuffer() = default;
|
~ScratchBuffer() = default;
|
||||||
ScratchBuffer(const ScratchBuffer&) = delete;
|
ScratchBuffer(const ScratchBuffer&) = delete;
|
||||||
@@ -64,7 +64,7 @@ public:
|
|||||||
/// The previously held data will remain intact.
|
/// The previously held data will remain intact.
|
||||||
void resize(size_type size) {
|
void resize(size_type size) {
|
||||||
if (size > buffer_capacity) {
|
if (size > buffer_capacity) {
|
||||||
auto new_buffer = Common::make_unique_for_overwrite<T[]>(size);
|
auto new_buffer = std::make_unique_for_overwrite<T[]>(size);
|
||||||
std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T));
|
std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T));
|
||||||
buffer = std::move(new_buffer);
|
buffer = std::move(new_buffer);
|
||||||
buffer_capacity = size;
|
buffer_capacity = size;
|
||||||
@@ -77,7 +77,7 @@ public:
|
|||||||
void resize_destructive(size_type size) {
|
void resize_destructive(size_type size) {
|
||||||
if (size > buffer_capacity) {
|
if (size > buffer_capacity) {
|
||||||
buffer_capacity = size;
|
buffer_capacity = size;
|
||||||
buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity);
|
buffer = std::make_unique_for_overwrite<T[]>(buffer_capacity);
|
||||||
}
|
}
|
||||||
last_requested_size = size;
|
last_requested_size = size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,21 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
|||||||
#ifndef MAP_NOCORE
|
#ifndef MAP_NOCORE
|
||||||
#define MAP_NOCORE 0
|
#define MAP_NOCORE 0
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef MADV_FREE
|
||||||
|
#define MADV_FREE MADV_DONTNEED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void DecommitVectorPage(uintptr_t base) noexcept {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
// Linux's MADV_DONTNEED zeros out pages for us
|
||||||
|
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED);
|
||||||
|
#else
|
||||||
|
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE);
|
||||||
|
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||||
if (auto page = HostPageSize; size % page != 0) {
|
if (auto page = HostPageSize; size % page != 0) {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ const u64 HostPageMask = ~(HostPageSize - 1);
|
|||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||||
|
void DecommitVectorPage(uintptr_t base) noexcept;
|
||||||
|
|
||||||
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -114,7 +115,7 @@ public:
|
|||||||
const u64 end_page = AlignUp(base, HostPageSize);
|
const u64 end_page = AlignUp(base, HostPageSize);
|
||||||
const u64 first_size = (std::min)(end_page, end) - base;
|
const u64 first_size = (std::min)(end_page, end) - base;
|
||||||
|
|
||||||
if (IsCommittedPage(start / sizeof(T))) {
|
if (IsCommittedPage(start)) {
|
||||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,11 +125,16 @@ public:
|
|||||||
base = end_page;
|
base = end_page;
|
||||||
|
|
||||||
for (u64 page = base; page < end; page += HostPageSize) {
|
for (u64 page = base; page < end; page += HostPageSize) {
|
||||||
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
|
auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T);
|
||||||
|
if (!IsCommittedPage(index)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
|
if (end - page >= HostPageSize) {
|
||||||
|
DecommitPage(index);
|
||||||
|
} else {
|
||||||
|
std::memset(reinterpret_cast<void*>(page), 0, end - page);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +189,14 @@ private:
|
|||||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr void DecommitPage(std::size_t index) noexcept {
|
||||||
|
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||||
|
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||||
|
|
||||||
|
committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release);
|
||||||
|
DecommitVectorPage(page);
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t alloc_size{};
|
std::size_t alloc_size{};
|
||||||
T* base_ptr{};
|
T* base_ptr{};
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ add_library(core STATIC
|
|||||||
core_timing.h
|
core_timing.h
|
||||||
cpu_manager.cpp
|
cpu_manager.cpp
|
||||||
cpu_manager.h
|
cpu_manager.h
|
||||||
|
launch_params.cpp
|
||||||
|
launch_params.h
|
||||||
crypto/aes_util.cpp
|
crypto/aes_util.cpp
|
||||||
crypto/aes_util.h
|
crypto/aes_util.h
|
||||||
crypto/ctr_encryption_layer.cpp
|
crypto/ctr_encryption_layer.cpp
|
||||||
@@ -1203,6 +1205,9 @@ endif()
|
|||||||
|
|
||||||
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
|
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
|
||||||
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
|
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
|
||||||
|
if (MSVC)
|
||||||
|
target_link_libraries(core PRIVATE getopt)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (BOOST_NO_HEADERS)
|
if (BOOST_NO_HEADERS)
|
||||||
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
|
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
|
||||||
|
|||||||
@@ -109,8 +109,7 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
|
|||||||
auto reference = std::make_unique<FileReference>();
|
auto reference = std::make_unique<FileReference>();
|
||||||
this->InsertReferenceIntoListLocked(*reference);
|
this->InsertReferenceIntoListLocked(*reference);
|
||||||
|
|
||||||
auto file = std::shared_ptr<RealVfsFile>(
|
auto file = std::make_shared<RealVfsFile>(*this, std::move(reference), path, perms, size, std::move(parent_path));
|
||||||
new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path)));
|
|
||||||
cache[path] = file;
|
cache[path] = file;
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
@@ -177,7 +176,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
|
|||||||
|
|
||||||
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
|
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
|
||||||
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
return std::make_shared<RealVfsDirectory>(*this, path, perms);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
||||||
@@ -185,7 +184,7 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode p
|
|||||||
if (!FS::CreateDirs(path)) {
|
if (!FS::CreateDirs(path)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
return std::make_shared<RealVfsDirectory>(*this, path, perms);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ class RealVfsFile : public VfsFile {
|
|||||||
friend class RealVfsFilesystem;
|
friend class RealVfsFilesystem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
|
||||||
|
const std::string& path, OpenMode perms = OpenMode::Read,
|
||||||
|
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
|
||||||
~RealVfsFile() override;
|
~RealVfsFile() override;
|
||||||
|
|
||||||
std::string GetName() const override;
|
std::string GetName() const override;
|
||||||
@@ -95,9 +98,6 @@ public:
|
|||||||
bool Rename(std::string_view name) override;
|
bool Rename(std::string_view name) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
|
|
||||||
const std::string& path, OpenMode perms = OpenMode::Read,
|
|
||||||
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
|
|
||||||
|
|
||||||
RealVfsFilesystem& base;
|
RealVfsFilesystem& base;
|
||||||
std::unique_ptr<FileReference> reference;
|
std::unique_ptr<FileReference> reference;
|
||||||
@@ -113,6 +113,8 @@ class RealVfsDirectory : public VfsDirectory {
|
|||||||
friend class RealVfsFilesystem;
|
friend class RealVfsFilesystem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
|
||||||
|
OpenMode perms = OpenMode::Read);
|
||||||
~RealVfsDirectory() override;
|
~RealVfsDirectory() override;
|
||||||
|
|
||||||
VirtualFile GetFileRelative(std::string_view relative_path) const override;
|
VirtualFile GetFileRelative(std::string_view relative_path) const override;
|
||||||
@@ -138,9 +140,6 @@ public:
|
|||||||
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
|
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
|
|
||||||
OpenMode perms = OpenMode::Read);
|
|
||||||
|
|
||||||
template <typename T, typename R>
|
template <typename T, typename R>
|
||||||
std::vector<std::shared_ptr<R>> IterateEntries() const;
|
std::vector<std::shared_ptr<R>> IterateEntries() const;
|
||||||
|
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
|||||||
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
||||||
s32 z_index = Background;
|
s32 z_index = Background;
|
||||||
if (is_overlay) {
|
if (is_overlay) {
|
||||||
z_index = Overlay;
|
z_index = this->IsOverlayOpenLocked(*applet) ? Overlay : Background;
|
||||||
} else if (inherited_foreground) {
|
} else if (inherited_foreground) {
|
||||||
z_index = is_obscured ? Foreground : ForegroundVisible;
|
z_index = is_obscured ? Foreground : ForegroundVisible;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ void LoopProcess(Core::System& system) {
|
|||||||
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
|
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
|
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
|
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system), 30);
|
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioRendererManagerForDebugger>(system), 30);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
|
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
|
||||||
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
|
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -24,7 +24,7 @@ IReceiverService::~IReceiverService() = default;
|
|||||||
|
|
||||||
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
|
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
|
||||||
LOG_DEBUG(Service_PSC, "called");
|
LOG_DEBUG(Service_PSC, "called");
|
||||||
*out_receiver = std::shared_ptr<IReceiver>(new IReceiver(system));
|
*out_receiver = std::make_shared<IReceiver>(system);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/logging.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "common/scm_rev.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "core/launch_params.h"
|
||||||
|
|
||||||
|
#undef _UNICODE
|
||||||
|
#include <getopt.h>
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept {
|
||||||
|
LaunchParams p{};
|
||||||
|
|
||||||
|
int option_index = 0;
|
||||||
|
static struct option long_options[] = {
|
||||||
|
// clang-format off
|
||||||
|
{"debug", no_argument, 0, 'd'},
|
||||||
|
{"config", required_argument, 0, 'c'},
|
||||||
|
{"fullscreen", no_argument, 0, 'f'},
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{"game", required_argument, 0, 'g'},
|
||||||
|
{"multiplayer", required_argument, 0, 'm'},
|
||||||
|
{"program", optional_argument, 0, 'p'},
|
||||||
|
{"user", required_argument, 0, 'u'},
|
||||||
|
{"version", no_argument, 0, 'v'},
|
||||||
|
{"input-profile", no_argument, 0, 'i'},
|
||||||
|
{"null-render", no_argument, 0, 'n'},
|
||||||
|
{"singlecore", no_argument, 0, 's'},
|
||||||
|
{"filter", no_argument, 0, 'x'},
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
// clang-format on
|
||||||
|
};
|
||||||
|
|
||||||
|
while (optind < argc) {
|
||||||
|
if (int arg = getopt_long(argc, argv, "dc:fhg:m:p:u:vinsx", long_options, &option_index); arg != -1) {
|
||||||
|
switch (char(arg)) {
|
||||||
|
case 'd':
|
||||||
|
p.override_gdb_port = uint16_t(atoi(optarg));
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
p.config_path = optarg;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
p.fullscreen = true;
|
||||||
|
LOG_INFO(Frontend, "Starting in fullscreen mode...");
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
p.print_help = true;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
p.filepath = std::string(optarg);
|
||||||
|
break;
|
||||||
|
case 'i': {
|
||||||
|
p.input_profile = std::string(optarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm': {
|
||||||
|
p.use_multiplayer = true;
|
||||||
|
const std::string str_arg(optarg);
|
||||||
|
// regex to check if the format is nickname:password@ip:port
|
||||||
|
// with optional :password
|
||||||
|
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
|
||||||
|
if (std::regex_match(str_arg, re)) {
|
||||||
|
std::smatch match;
|
||||||
|
std::regex_search(str_arg, match, re);
|
||||||
|
ASSERT(match.size() == 5);
|
||||||
|
p.nickname = match[1];
|
||||||
|
p.password = match[2];
|
||||||
|
p.address = match[3];
|
||||||
|
if (!match[4].str().empty()) {
|
||||||
|
p.port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
|
||||||
|
}
|
||||||
|
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
|
||||||
|
ASSERT(std::regex_match(p.nickname, nickname_re) && "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
|
||||||
|
ASSERT(!p.address.empty() && "Address is empty");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'p':
|
||||||
|
p.program_args = argv[optind];
|
||||||
|
++optind;
|
||||||
|
break;
|
||||||
|
case 'u': {
|
||||||
|
// Launch game with a specific user
|
||||||
|
bool argument_ok = isdigit(optarg[0]);
|
||||||
|
p.selected_user = atoi(optarg);
|
||||||
|
if (!argument_ok) {
|
||||||
|
// try to look it up by username, only finds the first username that matches.
|
||||||
|
auto const user_idx = system.GetProfileManager().GetUserIndex(optarg);
|
||||||
|
if (user_idx != std::nullopt) {
|
||||||
|
p.selected_user = user_idx.value();
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Frontend, "Invalid user argument '{}'", optarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (system.GetProfileManager().UserExistsIndex(*p.selected_user)) {
|
||||||
|
Settings::values.current_user = s32(*p.selected_user);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Frontend, "Selected user {} doesn't exist", *p.selected_user);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'v':
|
||||||
|
p.print_version = true;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
p.force_null_render = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
p.force_single_core = true;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
p.log_filter = argv[optind];
|
||||||
|
++optind;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// only kept due to shortcuts made by Qt frontend which use "-qlaunch"
|
||||||
|
if (strcmp(argv[optind], "-hlaunch") == 0) {
|
||||||
|
p.launch_hlaunch = true;
|
||||||
|
} else if (strcmp(argv[optind], "-qlaunch") == 0) {
|
||||||
|
p.launch_qlaunch = true;
|
||||||
|
} else if (strcmp(argv[optind], "-setup") == 0) {
|
||||||
|
p.launch_setup = true;
|
||||||
|
} else {
|
||||||
|
// qt feeds utf8 data, sdl frontend feeds raw utf16 data
|
||||||
|
#ifdef _WIN32
|
||||||
|
p.filepath = argv_w != nullptr
|
||||||
|
? Common::UTF16ToUTF8(argv_w[optind])
|
||||||
|
: argv[optind];
|
||||||
|
#else
|
||||||
|
p.filepath = argv[optind];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
optind++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.argv0 = argv[0];
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyLaunchParams(LaunchParams const& lp) noexcept {
|
||||||
|
// apply the log_filter setting
|
||||||
|
// the logger was initialized before and doesn't pick up the filter on its own
|
||||||
|
Common::Log::Filter filter{};
|
||||||
|
filter.ParseFilterString(lp.log_filter.value_or(Settings::values.log_filter.GetValue()));
|
||||||
|
Common::Log::SetGlobalFilter(filter);
|
||||||
|
|
||||||
|
if (!lp.program_args.empty()) {
|
||||||
|
Settings::values.program_args = lp.program_args;
|
||||||
|
}
|
||||||
|
if (!lp.input_profile.empty()) {
|
||||||
|
auto& players = Settings::values.players.GetValue();
|
||||||
|
players[0].profile_name = lp.input_profile;
|
||||||
|
}
|
||||||
|
if (lp.selected_user.has_value()) {
|
||||||
|
Settings::values.current_user = std::clamp(*lp.selected_user, 0, 7);
|
||||||
|
}
|
||||||
|
if (lp.override_gdb_port.has_value()) {
|
||||||
|
Settings::values.use_gdbstub = true;
|
||||||
|
Settings::values.gdbstub_port = *lp.override_gdb_port;
|
||||||
|
}
|
||||||
|
if (lp.force_single_core) {
|
||||||
|
Settings::values.use_multi_core = false;
|
||||||
|
}
|
||||||
|
if (lp.force_null_render) {
|
||||||
|
Settings::values.renderer_backend = Settings::RendererBackend::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lp.print_version) {
|
||||||
|
LOG_INFO(Frontend, "Eden {} {}", Common::g_scm_branch, Common::g_scm_desc);
|
||||||
|
}
|
||||||
|
if (lp.print_help) {
|
||||||
|
LOG_INFO(Frontend,
|
||||||
|
"Usage: {}"
|
||||||
|
" [options] <filename>\n"
|
||||||
|
"-c, --config Load the specified configuration file\n"
|
||||||
|
"-f, --fullscreen Start in fullscreen mode\n"
|
||||||
|
"-g, --game File path of the game to load\n"
|
||||||
|
"-h, --help Display this help and exit\n"
|
||||||
|
"-m, --multiplayer=nick:password@address:port"
|
||||||
|
" Nickname, password, address and port for multiplayer\n"
|
||||||
|
"-p, --program Pass following string as arguments to executable\n"
|
||||||
|
"-u, --user Select a specific user profile from 0 to 7\n"
|
||||||
|
"-d, --debug Run the GDB stub on a port from 1 to 65535\n"
|
||||||
|
"-v, --version Output version information and exit\n",
|
||||||
|
lp.argv0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "network/room.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
class System;
|
||||||
|
struct LaunchParams {
|
||||||
|
std::string argv0{};
|
||||||
|
std::string filepath{};
|
||||||
|
std::string nickname{};
|
||||||
|
std::string password{};
|
||||||
|
std::string address{};
|
||||||
|
std::string input_profile{};
|
||||||
|
std::string program_args{};
|
||||||
|
std::optional<std::string> config_path{};
|
||||||
|
std::optional<int> selected_user{};
|
||||||
|
std::optional<u16> override_gdb_port{};
|
||||||
|
std::optional<std::string> log_filter{};
|
||||||
|
u16 port = Network::DefaultRoomPort;
|
||||||
|
bool use_multiplayer = false;
|
||||||
|
bool fullscreen = false;
|
||||||
|
bool force_null_render = false;
|
||||||
|
bool force_single_core = false;
|
||||||
|
// print
|
||||||
|
bool print_version = false;
|
||||||
|
bool print_help = false;
|
||||||
|
// qt
|
||||||
|
bool launch_hlaunch = false;
|
||||||
|
bool launch_qlaunch = false;
|
||||||
|
bool launch_setup = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept;
|
||||||
|
void ApplyLaunchParams(LaunchParams const& lp) noexcept;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -45,6 +45,19 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
|
|||||||
AbstractPad{hid_core_.kernel},
|
AbstractPad{hid_core_.kernel},
|
||||||
}}
|
}}
|
||||||
{
|
{
|
||||||
|
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
|
||||||
|
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
||||||
|
auto& controller = controller_data[aruid_index][i];
|
||||||
|
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||||
|
Core::HID::ControllerUpdateCallback engine_callback{
|
||||||
|
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) {
|
||||||
|
ControllerUpdate(*kernel, type, i);
|
||||||
|
},
|
||||||
|
.is_npad_service = true,
|
||||||
|
};
|
||||||
|
controller.callback_key = controller.device->SetCallback(engine_callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
|
for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
|
||||||
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
|
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
|
||||||
}
|
}
|
||||||
@@ -93,16 +106,6 @@ Result NPad::Activate(u64 aruid) {
|
|||||||
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
||||||
auto& controller = controller_data[aruid_index][i];
|
auto& controller = controller_data[aruid_index][i];
|
||||||
controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
|
controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
|
||||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
|
||||||
if (!controller.callback_key) {
|
|
||||||
Core::HID::ControllerUpdateCallback engine_callback{
|
|
||||||
.on_change = [this, i](Core::HID::ControllerTriggerType type) {
|
|
||||||
ControllerUpdate(hid_core.kernel, type, i);
|
|
||||||
},
|
|
||||||
.is_npad_service = true,
|
|
||||||
};
|
|
||||||
controller.callback_key = controller.device->SetCallback(engine_callback);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefill controller buffers
|
// Prefill controller buffers
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -28,11 +31,6 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
|
|||||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <auto func, typename... Args>
|
|
||||||
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
|
||||||
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ArgType>
|
template <typename ArgType>
|
||||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||||
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
||||||
@@ -53,22 +51,11 @@ auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
|||||||
template <auto func, bool is_first_arg_inst, size_t... I>
|
template <auto func, bool is_first_arg_inst, size_t... I>
|
||||||
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
||||||
using Traits = FuncTraits<decltype(func)>;
|
using Traits = FuncTraits<decltype(func)>;
|
||||||
if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
|
|
||||||
if constexpr (is_first_arg_inst) {
|
|
||||||
SetDefinition<func>(
|
|
||||||
ctx, inst, *inst,
|
|
||||||
Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
|
||||||
} else {
|
|
||||||
SetDefinition<func>(
|
|
||||||
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if constexpr (is_first_arg_inst) {
|
if constexpr (is_first_arg_inst) {
|
||||||
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||||
} else {
|
} else {
|
||||||
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <auto func>
|
template <auto func>
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ void BufferCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
||||||
if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
|
if (IsRegionGpuModified(device_addr, size)) {
|
||||||
ClearDownload(device_addr, size);
|
ClearDownload(device_addr, size);
|
||||||
gpu_modified_ranges.Subtract(device_addr, size);
|
gpu_modified_ranges.Subtract(device_addr, size);
|
||||||
}
|
}
|
||||||
@@ -311,11 +311,8 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
|
|||||||
MarkWrittenBuffer(buffer_id, device_addr, size);
|
MarkWrittenBuffer(buffer_id, device_addr, size);
|
||||||
break;
|
break;
|
||||||
case ObtainBufferOperation::DiscardWrite: {
|
case ObtainBufferOperation::DiscardWrite: {
|
||||||
const DAddr device_addr_start = Common::AlignDown(device_addr, 64);
|
ClearDownload(device_addr, size);
|
||||||
const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
|
gpu_modified_ranges.Subtract(device_addr, size);
|
||||||
const size_t new_size = device_addr_end - device_addr_start;
|
|
||||||
ClearDownload(device_addr_start, new_size);
|
|
||||||
gpu_modified_ranges.Subtract(device_addr_start, new_size);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -1742,14 +1739,24 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si
|
|||||||
u64 total_size_bytes = 0;
|
u64 total_size_bytes = 0;
|
||||||
u64 largest_copy = 0;
|
u64 largest_copy = 0;
|
||||||
const DAddr buffer_start = buffer.cpu_addr_cached;
|
const DAddr buffer_start = buffer.cpu_addr_cached;
|
||||||
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
|
const auto add_upload = [&](DAddr start, DAddr end) {
|
||||||
|
if (start == end) return;
|
||||||
|
const u64 range_size = end - start;
|
||||||
upload_copies.push_back(BufferCopy{
|
upload_copies.push_back(BufferCopy{
|
||||||
.src_offset = total_size_bytes,
|
.src_offset = total_size_bytes,
|
||||||
.dst_offset = device_addr_out - buffer_start,
|
.dst_offset = start - buffer_start,
|
||||||
.size = range_size,
|
.size = range_size,
|
||||||
});
|
});
|
||||||
total_size_bytes += range_size;
|
total_size_bytes += range_size;
|
||||||
largest_copy = (std::max)(largest_copy, range_size);
|
largest_copy = (std::max)(largest_copy, range_size);
|
||||||
|
};
|
||||||
|
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
|
||||||
|
DAddr upload_start = device_addr_out;
|
||||||
|
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, [&](DAddr gpu_start, DAddr gpu_end) {
|
||||||
|
add_upload(upload_start, gpu_start);
|
||||||
|
upload_start = gpu_end;
|
||||||
|
});
|
||||||
|
add_upload(upload_start, device_addr_out + range_size);
|
||||||
});
|
});
|
||||||
if (total_size_bytes == 0) {
|
if (total_size_bytes == 0) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
constexpr u32 MacroRegistersStart = 0xE00;
|
constexpr u32 MacroRegistersStart = 0xE00;
|
||||||
[[maybe_unused]] constexpr u32 ComputeInline = 0x6D;
|
constexpr u32 ComputeInline = 0x6D;
|
||||||
|
|
||||||
DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
|
DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
|
||||||
: system{system_}
|
: system{system_}
|
||||||
@@ -73,11 +73,16 @@ bool DmaPusher::Step() {
|
|||||||
synced = false;
|
synced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.size > 0 && dma_state.method >= MacroRegistersStart && subchannels[dma_state.subchannel]) {
|
|
||||||
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, header.size * sizeof(u32));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header.size > 0) {
|
if (header.size > 0) {
|
||||||
|
if (subchannels[dma_state.subchannel] && dma_state.method_count) {
|
||||||
|
const auto engine = subchannel_type[dma_state.subchannel];
|
||||||
|
const bool kepler_payload = engine == Engines::EngineTypes::KeplerCompute && dma_state.method == ComputeInline && dma_state.non_incrementing;
|
||||||
|
const bool macro_payload = engine == Engines::EngineTypes::Maxwell3D && dma_state.method >= MacroRegistersStart;
|
||||||
|
if (kepler_payload || macro_payload) {
|
||||||
|
const size_t words = std::min<size_t>(dma_state.method_count, header.size);
|
||||||
|
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, words * sizeof(u32));
|
||||||
|
}
|
||||||
|
}
|
||||||
const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe();
|
const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe();
|
||||||
if (use_safe) {
|
if (use_safe) {
|
||||||
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers);
|
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers);
|
||||||
|
|||||||
@@ -49,13 +49,16 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu
|
|||||||
case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
|
case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
|
||||||
UploadInfo info{.upload_address = upload_address,
|
UploadInfo info{.upload_address = upload_address,
|
||||||
.exec_address = upload_state.ExecTargetAddress(),
|
.exec_address = upload_state.ExecTargetAddress(),
|
||||||
.copy_size = upload_state.GetUploadSize()};
|
.copy_size = upload_state.GetUploadSize(),
|
||||||
|
.was_dirty = upload_dirty};
|
||||||
uploads.push_back(info);
|
uploads.push_back(info);
|
||||||
upload_state.ProcessExec(regs.exec_upload.linear != 0);
|
upload_state.ProcessExec(regs.exec_upload.linear != 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KEPLER_COMPUTE_REG_INDEX(data_upload): {
|
case KEPLER_COMPUTE_REG_INDEX(data_upload): {
|
||||||
upload_address = current_dma_segment;
|
upload_address = current_dma_segment;
|
||||||
|
upload_dirty = current_dirty;
|
||||||
|
current_dirty = false;
|
||||||
upload_state.ProcessData(method_argument, is_last_call);
|
upload_state.ProcessData(method_argument, is_last_call);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -64,11 +67,13 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu
|
|||||||
|
|
||||||
for (auto& data : uploads) {
|
for (auto& data : uploads) {
|
||||||
const GPUVAddr offset = data.exec_address - launch_desc_loc;
|
const GPUVAddr offset = data.exec_address - launch_desc_loc;
|
||||||
if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x) &&
|
if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x)) {
|
||||||
memory_manager.IsMemoryDirty(data.upload_address, data.copy_size)) {
|
const bool source_dirty = memory_manager.IsMemoryDirty(data.upload_address, data.copy_size);
|
||||||
|
if (data.was_dirty || source_dirty) {
|
||||||
indirect_compute = {data.upload_address};
|
indirect_compute = {data.upload_address};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
uploads.clear();
|
uploads.clear();
|
||||||
ProcessLaunch();
|
ProcessLaunch();
|
||||||
indirect_compute = std::nullopt;
|
indirect_compute = std::nullopt;
|
||||||
@@ -83,6 +88,8 @@ void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32*
|
|||||||
switch (method) {
|
switch (method) {
|
||||||
case KEPLER_COMPUTE_REG_INDEX(data_upload):
|
case KEPLER_COMPUTE_REG_INDEX(data_upload):
|
||||||
upload_address = current_dma_segment;
|
upload_address = current_dma_segment;
|
||||||
|
upload_dirty = current_dirty;
|
||||||
|
current_dirty = false;
|
||||||
upload_state.ProcessData(base_start, amount);
|
upload_state.ProcessData(base_start, amount);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -226,11 +226,13 @@ private:
|
|||||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||||
Upload::State upload_state;
|
Upload::State upload_state;
|
||||||
GPUVAddr upload_address;
|
GPUVAddr upload_address;
|
||||||
|
bool upload_dirty{};
|
||||||
|
|
||||||
struct UploadInfo {
|
struct UploadInfo {
|
||||||
GPUVAddr upload_address;
|
GPUVAddr upload_address;
|
||||||
GPUVAddr exec_address;
|
GPUVAddr exec_address;
|
||||||
u32 copy_size;
|
u32 copy_size;
|
||||||
|
bool was_dirty;
|
||||||
};
|
};
|
||||||
std::vector<UploadInfo> uploads;
|
std::vector<UploadInfo> uploads;
|
||||||
std::optional<GPUVAddr> indirect_compute{};
|
std::optional<GPUVAddr> indirect_compute{};
|
||||||
|
|||||||
@@ -94,11 +94,14 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
|
|||||||
const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
|
const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
|
||||||
const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
|
const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
|
||||||
const bool use_accelerated = texture_info.has_value();
|
const bool use_accelerated = texture_info.has_value();
|
||||||
|
const bool is_applet =
|
||||||
|
(framebuffer.layer_stack_mask & Service::Nvnflinger::LayerStackBit(
|
||||||
|
Service::Nvnflinger::LayerStackId::Recording)) == 0;
|
||||||
|
|
||||||
RefreshResources(device, framebuffer);
|
RefreshResources(device, framebuffer);
|
||||||
SetAntiAliasPass(device);
|
SetAntiAliasPass(device);
|
||||||
#ifdef HAS_RESHADE
|
#ifdef HAS_RESHADE
|
||||||
SetPostProcessPass(device);
|
SetPostProcessPass(device, is_applet);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Finish any pending renderpass
|
// Finish any pending renderpass
|
||||||
@@ -138,9 +141,12 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
|
|||||||
source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
||||||
crop_rect = {0, 0, 1, 1};
|
crop_rect = {0, 0, 1, 1};
|
||||||
} else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) {
|
} else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) {
|
||||||
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
if (!is_applet) {
|
||||||
|
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image,
|
||||||
|
source_image_view, render_extent, crop_rect);
|
||||||
crop_rect = {0, 0, 1, 1};
|
crop_rect = {0, 0, 1, 1};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SetMatrixData(device, *out_push_constants, layout);
|
SetMatrixData(device, *out_push_constants, layout);
|
||||||
SetVertexData(device, *out_push_constants, layout, crop_rect);
|
SetVertexData(device, *out_push_constants, layout, crop_rect);
|
||||||
@@ -228,7 +234,11 @@ void Layer::SetAntiAliasPass(const Device& device) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_RESHADE
|
#ifdef HAS_RESHADE
|
||||||
void Layer::SetPostProcessPass(const Device& device) {
|
void Layer::SetPostProcessPass(const Device& device, bool is_applet) {
|
||||||
|
if (is_applet) {
|
||||||
|
post_process.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const VkExtent2D render_area{
|
const VkExtent2D render_area{
|
||||||
.width = Settings::values.resolution_info.ScaleUp(raw_width),
|
.width = Settings::values.resolution_info.ScaleUp(raw_width),
|
||||||
.height = Settings::values.resolution_info.ScaleUp(raw_height),
|
.height = Settings::values.resolution_info.ScaleUp(raw_height),
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ private:
|
|||||||
void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer);
|
void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer);
|
||||||
void SetAntiAliasPass(const Device& device);
|
void SetAntiAliasPass(const Device& device);
|
||||||
#ifdef HAS_RESHADE
|
#ifdef HAS_RESHADE
|
||||||
void SetPostProcessPass(const Device& device);
|
void SetPostProcessPass(const Device& device, bool is_applet);
|
||||||
#endif
|
#endif
|
||||||
void ReleaseRawImages();
|
void ReleaseRawImages();
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ public:
|
|||||||
if (host_visible) {
|
if (host_visible) {
|
||||||
return StagingBufferRef{};
|
return StagingBufferRef{};
|
||||||
}
|
}
|
||||||
return staging_pool.Request(size_bytes, MemoryUsage::Upload);
|
return staging_pool.Request(device, size_bytes, MemoryUsage::Upload);
|
||||||
}();
|
}();
|
||||||
|
|
||||||
u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data();
|
u8* staging_data = host_visible ? buffer.Mapped().data() : staging.mapped_span.data();
|
||||||
@@ -375,11 +375,11 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
|
|||||||
}
|
}
|
||||||
|
|
||||||
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
|
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
|
||||||
return staging_pool.Request(size, MemoryUsage::Upload);
|
return staging_pool.Request(device, size, MemoryUsage::Upload);
|
||||||
}
|
}
|
||||||
|
|
||||||
StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
|
StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
|
||||||
return staging_pool.Request(size, MemoryUsage::Download, deferred);
|
return staging_pool.Request(device, size, MemoryUsage::Download, deferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkFormat BufferCacheRuntime::TexelBufferFormat(VideoCore::Surface::PixelFormat format) const {
|
VkFormat BufferCacheRuntime::TexelBufferFormat(VideoCore::Surface::PixelFormat format) const {
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ public:
|
|||||||
std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
|
std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
|
||||||
[[maybe_unused]] u32 binding_index,
|
[[maybe_unused]] u32 binding_index,
|
||||||
u32 size) {
|
u32 size) {
|
||||||
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload);
|
const StagingBufferRef ref = staging_pool.Request(device, size, MemoryUsage::Upload);
|
||||||
guest_descriptor_queue.AddBuffer(ref.buffer, ref.device_address,
|
guest_descriptor_queue.AddBuffer(ref.buffer, ref.device_address,
|
||||||
static_cast<u32>(ref.offset), size);
|
static_cast<u32>(ref.offset), size);
|
||||||
return ref.mapped_span;
|
return ref.mapped_span;
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ Uint8Pass::~Uint8Pass() = default;
|
|||||||
std::pair<VkBuffer, VkDeviceSize> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
|
std::pair<VkBuffer, VkDeviceSize> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
|
||||||
u32 src_offset) {
|
u32 src_offset) {
|
||||||
const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
|
const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
|
||||||
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
|
const auto staging = staging_buffer_pool.Request(device, staging_size, MemoryUsage::DeviceLocal);
|
||||||
|
|
||||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||||
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
|
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
|
||||||
@@ -345,7 +345,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
|
|||||||
const u32 num_tri_vertices = (is_strip ? (num_vertices - 2) / 2 : num_vertices / 4) * 6;
|
const u32 num_tri_vertices = (is_strip ? (num_vertices - 2) / 2 : num_vertices / 4) * 6;
|
||||||
|
|
||||||
const std::size_t staging_size = num_tri_vertices * sizeof(u32);
|
const std::size_t staging_size = num_tri_vertices * sizeof(u32);
|
||||||
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
|
const auto staging = staging_buffer_pool.Request(device, staging_size, MemoryUsage::DeviceLocal);
|
||||||
|
|
||||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||||
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
|
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
|
||||||
|
|||||||
@@ -852,7 +852,7 @@ public:
|
|||||||
|
|
||||||
void PushUnsyncedQueries() override {
|
void PushUnsyncedQueries() override {
|
||||||
CloseCounter();
|
CloseCounter();
|
||||||
auto staging_ref = staging_pool.Request(
|
auto staging_ref = staging_pool.Request(device,
|
||||||
pending_flush_queries.size() * TFBQueryBank::QUERY_SIZE, MemoryUsage::Download, true);
|
pending_flush_queries.size() * TFBQueryBank::QUERY_SIZE, MemoryUsage::Download, true);
|
||||||
size_t offset_base = staging_ref.offset;
|
size_t offset_base = staging_ref.offset;
|
||||||
for (auto q : pending_flush_queries) {
|
for (auto q : pending_flush_queries) {
|
||||||
@@ -1657,7 +1657,7 @@ void QueryCacheRuntime::SyncValues(std::span<SyncValuesType> values, VkBuffer ba
|
|||||||
impl->copies_setup.clear();
|
impl->copies_setup.clear();
|
||||||
impl->copies_setup.resize(impl->little_cache.size());
|
impl->copies_setup.resize(impl->little_cache.size());
|
||||||
if constexpr (SyncValuesType::GeneratesBaseBuffer) {
|
if constexpr (SyncValuesType::GeneratesBaseBuffer) {
|
||||||
ref = impl->staging_pool.Request(total_size, MemoryUsage::Upload);
|
ref = impl->staging_pool.Request(impl->device, total_size, MemoryUsage::Upload);
|
||||||
size_t current_offset = ref.offset;
|
size_t current_offset = ref.offset;
|
||||||
size_t accumulated_size = 0;
|
size_t accumulated_size = 0;
|
||||||
for (size_t i = 0; i < values.size(); i++) {
|
for (size_t i = 0; i < values.size(); i++) {
|
||||||
|
|||||||
@@ -28,55 +28,42 @@ using namespace Common::Literals;
|
|||||||
// Maximum potential alignment of a Vulkan buffer
|
// Maximum potential alignment of a Vulkan buffer
|
||||||
constexpr VkDeviceSize MAX_ALIGNMENT = 256;
|
constexpr VkDeviceSize MAX_ALIGNMENT = 256;
|
||||||
|
|
||||||
// Stream buffer size in bytes
|
size_t GetStreamBufferSize(const Device& device, size_t max_stream_buffer_size, size_t max_alignment) {
|
||||||
// *NIX drivers are more sensitive to increased buffers for streaming.
|
|
||||||
// Windows ones however, can intake bigger buffers and generally do not OOM.
|
|
||||||
// - GTX 960 on Windows will not OOM with 256mib
|
|
||||||
// - GT 1030 on ^NIX will OOM with 256mib
|
|
||||||
#if defined(__FreeBSD__)
|
|
||||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
|
|
||||||
#else
|
|
||||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t GetStreamBufferSize(const Device& device) {
|
|
||||||
if (!device.HasDebuggingToolAttached()) {
|
if (!device.HasDebuggingToolAttached()) {
|
||||||
return MAX_STREAM_BUFFER_SIZE;
|
return max_stream_buffer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkDeviceSize size{0};
|
VkDeviceSize size{0};
|
||||||
bool has_device_local_host_visible_heap{};
|
bool has_device_local_host_visible_heap{};
|
||||||
ForEachDeviceLocalHostVisibleHeap(device, [&size, &has_device_local_host_visible_heap](
|
ForEachDeviceLocalHostVisibleHeap(device, [&size, &has_device_local_host_visible_heap](size_t index, VkMemoryHeap& heap) {
|
||||||
size_t index, VkMemoryHeap& heap) {
|
|
||||||
has_device_local_host_visible_heap = true;
|
has_device_local_host_visible_heap = true;
|
||||||
size = (std::max)(size, heap.size);
|
size = std::max<size_t>(size, heap.size);
|
||||||
});
|
});
|
||||||
if (has_device_local_host_visible_heap) {
|
if (has_device_local_host_visible_heap) {
|
||||||
// If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be
|
// If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be
|
||||||
// loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue
|
// loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue
|
||||||
// as the heap will be much larger.
|
// as the heap will be much larger.
|
||||||
if (size <= MAX_STREAM_BUFFER_SIZE) {
|
if (size <= max_stream_buffer_size) {
|
||||||
size = size * 40 / 100;
|
size = size * 40 / 100;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
size = MAX_STREAM_BUFFER_SIZE;
|
size = max_stream_buffer_size;
|
||||||
}
|
}
|
||||||
return (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE);
|
return std::min<size_t>(Common::AlignUp(size, max_alignment), max_stream_buffer_size);
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
|
StagingBufferPool::StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator_, Scheduler& scheduler_)
|
||||||
Scheduler& scheduler_)
|
: memory_allocator{memory_allocator_}, scheduler{scheduler_}
|
||||||
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
|
, stream_buffer_size{GetStreamBufferSize(device, 256_MiB, MAX_ALIGNMENT)}
|
||||||
stream_buffer_size{GetStreamBufferSize(device)}, region_size{stream_buffer_size /
|
{
|
||||||
StagingBufferPool::NUM_SYNCS} {
|
|
||||||
VkBufferCreateInfo stream_ci = {
|
VkBufferCreateInfo stream_ci = {
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.size = stream_buffer_size,
|
.size = stream_buffer_size,
|
||||||
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
|
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
|
||||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
.queueFamilyIndexCount = 0,
|
.queueFamilyIndexCount = 0,
|
||||||
.pQueueFamilyIndices = nullptr,
|
.pQueueFamilyIndices = nullptr,
|
||||||
@@ -87,7 +74,16 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
|
|||||||
if (device.IsBufferDeviceAddressSupported()) {
|
if (device.IsBufferDeviceAddressSupported()) {
|
||||||
stream_ci.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
stream_ci.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
||||||
}
|
}
|
||||||
|
// Some drivers are more sensitive to increased buffer sizes
|
||||||
|
try {
|
||||||
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
|
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
|
||||||
|
} catch (vk::Exception& e) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Can't fit {} bytes buffer, halving", stream_ci.size);
|
||||||
|
stream_buffer_size = GetStreamBufferSize(device, 128_MiB, MAX_ALIGNMENT);
|
||||||
|
stream_ci.size = stream_buffer_size;
|
||||||
|
stream_buffer = memory_allocator.CreateBuffer(stream_ci, MemoryUsage::Stream);
|
||||||
|
}
|
||||||
|
region_size = stream_buffer_size / StagingBufferPool::NUM_SYNCS;
|
||||||
if (device.HasDebuggingToolAttached()) {
|
if (device.HasDebuggingToolAttached()) {
|
||||||
stream_buffer.SetObjectNameEXT("Stream Buffer");
|
stream_buffer.SetObjectNameEXT("Stream Buffer");
|
||||||
}
|
}
|
||||||
@@ -100,11 +96,10 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
|
|||||||
|
|
||||||
StagingBufferPool::~StagingBufferPool() = default;
|
StagingBufferPool::~StagingBufferPool() = default;
|
||||||
|
|
||||||
StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) {
|
StagingBufferRef StagingBufferPool::Request(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
|
||||||
if (!deferred && usage == MemoryUsage::Upload && size <= region_size) {
|
return (!deferred && usage == MemoryUsage::Upload && size <= region_size)
|
||||||
return GetStreamBuffer(size);
|
? GetStreamBuffer(device, size)
|
||||||
}
|
: GetStagingBuffer(device, size, usage, deferred);
|
||||||
return GetStagingBuffer(size, usage, deferred);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) {
|
void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) {
|
||||||
@@ -127,11 +122,10 @@ void StagingBufferPool::TickFrame() {
|
|||||||
ReleaseCache(MemoryUsage::Download);
|
ReleaseCache(MemoryUsage::Download);
|
||||||
}
|
}
|
||||||
|
|
||||||
StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
|
StagingBufferRef StagingBufferPool::GetStreamBuffer(const Device& device, size_t size) {
|
||||||
if (AreRegionsActive(Region(free_iterator) + 1,
|
if (AreRegionsActive(Region(free_iterator) + 1, (std::min)(Region(iterator + size) + 1, NUM_SYNCS))) {
|
||||||
(std::min)(Region(iterator + size) + 1, NUM_SYNCS))) {
|
|
||||||
// Avoid waiting for the previous usages to be free
|
// Avoid waiting for the previous usages to be free
|
||||||
return GetStagingBuffer(size, MemoryUsage::Upload);
|
return GetStagingBuffer(device, size, MemoryUsage::Upload);
|
||||||
}
|
}
|
||||||
const u64 current_tick = scheduler.CurrentTick();
|
const u64 current_tick = scheduler.CurrentTick();
|
||||||
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator),
|
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator),
|
||||||
@@ -140,15 +134,14 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
|
|||||||
free_iterator = (std::max)(free_iterator, iterator + size);
|
free_iterator = (std::max)(free_iterator, iterator + size);
|
||||||
|
|
||||||
if (iterator + size >= stream_buffer_size) {
|
if (iterator + size >= stream_buffer_size) {
|
||||||
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS,
|
std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, current_tick);
|
||||||
current_tick);
|
|
||||||
used_iterator = 0;
|
used_iterator = 0;
|
||||||
iterator = 0;
|
iterator = 0;
|
||||||
free_iterator = size;
|
free_iterator = size;
|
||||||
|
|
||||||
if (AreRegionsActive(0, Region(size) + 1)) {
|
if (AreRegionsActive(0, Region(size) + 1)) {
|
||||||
// Avoid waiting for the previous usages to be free
|
// Avoid waiting for the previous usages to be free
|
||||||
return GetStagingBuffer(size, MemoryUsage::Upload);
|
return GetStagingBuffer(device, size, MemoryUsage::Upload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const size_t offset = iterator;
|
const size_t offset = iterator;
|
||||||
@@ -156,7 +149,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
|
|||||||
return StagingBufferRef{
|
return StagingBufferRef{
|
||||||
.buffer = *stream_buffer,
|
.buffer = *stream_buffer,
|
||||||
.device_address = stream_buffer_address,
|
.device_address = stream_buffer_address,
|
||||||
.offset = static_cast<VkDeviceSize>(offset),
|
.offset = VkDeviceSize(offset),
|
||||||
.mapped_span = stream_pointer.subspan(offset, size),
|
.mapped_span = stream_pointer.subspan(offset, size),
|
||||||
.usage{},
|
.usage{},
|
||||||
.log2_level{},
|
.log2_level{},
|
||||||
@@ -166,21 +159,18 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
|
|||||||
|
|
||||||
bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end) const {
|
bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end) const {
|
||||||
const u64 gpu_tick = scheduler.GetMasterSemaphore().KnownGpuTick();
|
const u64 gpu_tick = scheduler.GetMasterSemaphore().KnownGpuTick();
|
||||||
return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end,
|
return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end, [gpu_tick](u64 sync_tick) {
|
||||||
[gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; });
|
return gpu_tick < sync_tick;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage,
|
StagingBufferRef StagingBufferPool::GetStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
|
||||||
bool deferred) {
|
if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred))
|
||||||
if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage, deferred)) {
|
|
||||||
return *ref;
|
return *ref;
|
||||||
}
|
return CreateStagingBuffer(device, size, usage, deferred);
|
||||||
return CreateStagingBuffer(size, usage, deferred);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size,
|
std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, MemoryUsage usage, bool deferred) {
|
||||||
MemoryUsage usage,
|
|
||||||
bool deferred) {
|
|
||||||
StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil(size)];
|
StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil(size)];
|
||||||
|
|
||||||
const auto is_free = [this](const StagingBuffer& entry) {
|
const auto is_free = [this](const StagingBuffer& entry) {
|
||||||
@@ -202,7 +192,7 @@ std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t s
|
|||||||
return it->Ref();
|
return it->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred) {
|
StagingBufferRef StagingBufferPool::CreateStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred) {
|
||||||
auto const log2_size = Common::Log2Ceil<u32>(u32(size));
|
auto const log2_size = Common::Log2Ceil<u32>(u32(size));
|
||||||
VkBufferCreateInfo buffer_ci = {
|
VkBufferCreateInfo buffer_ci = {
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||||
|
|||||||
@@ -33,11 +33,10 @@ class StagingBufferPool {
|
|||||||
public:
|
public:
|
||||||
static constexpr size_t NUM_SYNCS = 16;
|
static constexpr size_t NUM_SYNCS = 16;
|
||||||
|
|
||||||
explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator,
|
explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler);
|
||||||
Scheduler& scheduler);
|
|
||||||
~StagingBufferPool();
|
~StagingBufferPool();
|
||||||
|
|
||||||
StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false);
|
StagingBufferRef Request(const Device& device, size_t size, MemoryUsage usage, bool deferred = false);
|
||||||
void FreeDeferred(StagingBufferRef& ref);
|
void FreeDeferred(StagingBufferRef& ref);
|
||||||
|
|
||||||
[[nodiscard]] VkBuffer StreamBuf() const noexcept {
|
[[nodiscard]] VkBuffer StreamBuf() const noexcept {
|
||||||
@@ -84,27 +83,18 @@ private:
|
|||||||
static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT;
|
static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT;
|
||||||
using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>;
|
using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>;
|
||||||
|
|
||||||
StagingBufferRef GetStreamBuffer(size_t size);
|
StagingBufferRef GetStreamBuffer(const Device& device, size_t size);
|
||||||
|
|
||||||
bool AreRegionsActive(size_t region_begin, size_t region_end) const;
|
bool AreRegionsActive(size_t region_begin, size_t region_end) const;
|
||||||
|
StagingBufferRef GetStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred = false);
|
||||||
StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false);
|
std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage, bool deferred);
|
||||||
|
StagingBufferRef CreateStagingBuffer(const Device& device, size_t size, MemoryUsage usage, bool deferred);
|
||||||
std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage,
|
|
||||||
bool deferred);
|
|
||||||
|
|
||||||
StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred);
|
|
||||||
|
|
||||||
StagingBuffersCache& GetCache(MemoryUsage usage);
|
StagingBuffersCache& GetCache(MemoryUsage usage);
|
||||||
|
|
||||||
void ReleaseCache(MemoryUsage usage);
|
void ReleaseCache(MemoryUsage usage);
|
||||||
|
|
||||||
void ReleaseLevel(StagingBuffersCache& cache, size_t log2);
|
void ReleaseLevel(StagingBuffersCache& cache, size_t log2);
|
||||||
size_t Region(size_t iter) const noexcept {
|
size_t Region(size_t iter) const noexcept {
|
||||||
return iter / region_size;
|
return iter / region_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Device& device;
|
|
||||||
MemoryAllocator& memory_allocator;
|
MemoryAllocator& memory_allocator;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
|
|
||||||
|
|||||||
@@ -975,11 +975,11 @@ void TextureCacheRuntime::Finish() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size, bool deferred) {
|
StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size, bool deferred) {
|
||||||
return staging_buffer_pool.Request(size, MemoryUsage::Upload, deferred);
|
return staging_buffer_pool.Request(device, size, MemoryUsage::Upload, deferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
|
StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) {
|
||||||
return staging_buffer_pool.Request(size, MemoryUsage::Download, deferred);
|
return staging_buffer_pool.Request(device, size, MemoryUsage::Download, deferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) {
|
void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) {
|
||||||
|
|||||||
+16
-66
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
// Static Qt on macOS doesn't use Vulkan
|
// Static Qt on macOS doesn't use Vulkan
|
||||||
// Other platforms do, and thus have conflicting VulkanMemoryAllocator symbols
|
// Other platforms do, and thus have conflicting VulkanMemoryAllocator symbols
|
||||||
|
#include "core/launch_params.h"
|
||||||
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
|
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
|
||||||
#undef VMA_IMPLEMENTATION
|
#undef VMA_IMPLEMENTATION
|
||||||
#endif
|
#endif
|
||||||
@@ -515,79 +516,28 @@ MainWindow::MainWindow(bool has_broken_vulkan)
|
|||||||
// to prevent the UI from blowing up.
|
// to prevent the UI from blowing up.
|
||||||
UpdateUITheme();
|
UpdateUITheme();
|
||||||
|
|
||||||
QStringList args = QApplication::arguments();
|
if (QStringList args = QApplication::arguments(); args.size() >= 2) {
|
||||||
|
QList<QByteArray> qba{};
|
||||||
|
for (auto const& e : args)
|
||||||
|
qba.push_back(e.toUtf8());
|
||||||
|
std::vector<char*> args_cstr{};
|
||||||
|
for (auto& e : qba)
|
||||||
|
args_cstr.push_back(e.data());
|
||||||
|
|
||||||
if (args.size() < 2) {
|
auto const lp = Core::ParseLaunchParams(*QtCommon::system, args_cstr.size(), args_cstr.data(), nullptr);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString game_path;
|
|
||||||
bool should_launch_qlaunch = false;
|
|
||||||
bool should_launch_hlaunch = false;
|
|
||||||
bool should_launch_setup = false;
|
|
||||||
bool has_gamepath = false;
|
|
||||||
bool is_fullscreen = false;
|
|
||||||
|
|
||||||
// Preserves drag/drop functionality
|
|
||||||
for (int i = 1; i < args.size(); ++i) {
|
|
||||||
if (args[i] == QStringLiteral("-f")) {
|
|
||||||
// Launch game in fullscreen mode
|
|
||||||
is_fullscreen = true;
|
|
||||||
} else if (args[i] == QStringLiteral("-u") && i < args.size() - 1) {
|
|
||||||
// Launch game with a specific user
|
|
||||||
int user_arg_idx = ++i;
|
|
||||||
bool argument_ok;
|
|
||||||
std::size_t selected_user = args[user_arg_idx].toUInt(&argument_ok);
|
|
||||||
if (!argument_ok) {
|
|
||||||
// try to look it up by username, only finds the first username that matches.
|
|
||||||
std::string const user_arg_str = args[user_arg_idx].toStdString();
|
|
||||||
auto const user_idx =
|
|
||||||
QtCommon::system->GetProfileManager().GetUserIndex(user_arg_str);
|
|
||||||
if (user_idx != std::nullopt) {
|
|
||||||
selected_user = user_idx.value();
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Frontend, "Invalid user argument '{}'", user_arg_str);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (QtCommon::system->GetProfileManager().UserExistsIndex(selected_user)) {
|
|
||||||
Settings::values.current_user = s32(selected_user);
|
|
||||||
user_flag_cmd_line = true;
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Frontend, "Selected user {} doesn't exist", selected_user);
|
|
||||||
}
|
|
||||||
} else if (args[i] == QStringLiteral("-g") && i < args.size() - 1) {
|
|
||||||
// Launch game at path
|
|
||||||
game_path = args[++i];
|
|
||||||
has_gamepath = true;
|
|
||||||
} else if (args[i] == QStringLiteral("-input-profile") && i < args.size() - 1) {
|
|
||||||
auto& players = Settings::values.players.GetValue();
|
|
||||||
players[0].profile_name = args[++i].toStdString();
|
|
||||||
} else if (args[i] == QStringLiteral("-qlaunch")) {
|
|
||||||
should_launch_qlaunch = true;
|
|
||||||
} else if (args[i] == QStringLiteral("-hlaunch")) {
|
|
||||||
should_launch_hlaunch = true;
|
|
||||||
} else if (args[i] == QStringLiteral("-setup")) {
|
|
||||||
should_launch_setup = true;
|
|
||||||
} else {
|
|
||||||
game_path = args[i];
|
|
||||||
has_gamepath = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override fullscreen setting if gamepath or argument is provided
|
// Override fullscreen setting if gamepath or argument is provided
|
||||||
if (has_gamepath || is_fullscreen) {
|
if (!lp.filepath.empty() || lp.fullscreen) {
|
||||||
ui->action_Fullscreen->setChecked(is_fullscreen);
|
ui->action_Fullscreen->setChecked(lp.fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (should_launch_setup) {
|
if (!lp.filepath.empty()) {
|
||||||
|
BootGame(QString::fromStdString(lp.filepath), ApplicationAppletParameters());
|
||||||
|
} else if (lp.launch_setup) {
|
||||||
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::Starter), std::nullopt);
|
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::Starter), std::nullopt);
|
||||||
} else {
|
} else if (lp.launch_qlaunch) {
|
||||||
if (!game_path.isEmpty()) {
|
|
||||||
BootGame(game_path, ApplicationAppletParameters());
|
|
||||||
} else if (should_launch_qlaunch) {
|
|
||||||
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt);
|
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt);
|
||||||
} else if (should_launch_hlaunch) {
|
} else if (lp.launch_hlaunch) {
|
||||||
std::filesystem::path const sd_dir =
|
std::filesystem::path const sd_dir =
|
||||||
Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir);
|
Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir);
|
||||||
auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string();
|
auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string();
|
||||||
|
|||||||
@@ -42,9 +42,6 @@ target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common
|
|||||||
if (ENABLE_OPENGL)
|
if (ENABLE_OPENGL)
|
||||||
target_link_libraries(yuzu-cmd PRIVATE glad)
|
target_link_libraries(yuzu-cmd PRIVATE glad)
|
||||||
endif()
|
endif()
|
||||||
if (MSVC)
|
|
||||||
target_link_libraries(yuzu-cmd PRIVATE getopt)
|
|
||||||
endif()
|
|
||||||
target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
create_resource("../../dist/eden.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon")
|
create_resource("../../dist/eden.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon")
|
||||||
|
|||||||
+15
-189
@@ -10,6 +10,7 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "common/settings_enums.h"
|
#include "common/settings_enums.h"
|
||||||
|
#include "core/launch_params.h"
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -52,12 +53,6 @@
|
|||||||
#include "common/windows/timer_resolution.h"
|
#include "common/windows/timer_resolution.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#undef _UNICODE
|
|
||||||
#include <getopt.h>
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
|
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
|
||||||
@@ -67,25 +62,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void PrintHelp(const char* argv0) {
|
|
||||||
std::cout << "Usage: " << argv0
|
|
||||||
<< " [options] <filename>\n"
|
|
||||||
"-c, --config Load the specified configuration file\n"
|
|
||||||
"-f, --fullscreen Start in fullscreen mode\n"
|
|
||||||
"-g, --game File path of the game to load\n"
|
|
||||||
"-h, --help Display this help and exit\n"
|
|
||||||
"-m, --multiplayer=nick:password@address:port"
|
|
||||||
" Nickname, password, address and port for multiplayer\n"
|
|
||||||
"-p, --program Pass following string as arguments to executable\n"
|
|
||||||
"-u, --user Select a specific user profile from 0 to 7\n"
|
|
||||||
"-d, --debug Run the GDB stub on a port from 1 to 65535\n"
|
|
||||||
"-v, --version Output version information and exit\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PrintVersion() {
|
|
||||||
std::cout << "Eden " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnStateChanged(const Network::RoomMember::State& state) {
|
static void OnStateChanged(const Network::RoomMember::State& state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Network::RoomMember::State::Idle:
|
case Network::RoomMember::State::Idle:
|
||||||
@@ -208,168 +184,18 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
|||||||
LOG_CRITICAL(Frontend, "Failed to get command line arguments");
|
LOG_CRITICAL(Frontend, "Failed to get command line arguments");
|
||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
std::string filepath;
|
|
||||||
std::optional<std::string> config_path{};
|
|
||||||
std::string program_args;
|
|
||||||
std::optional<int> selected_user{};
|
|
||||||
std::optional<u16> override_gdb_port{};
|
|
||||||
bool use_multiplayer = false;
|
|
||||||
bool fullscreen = false;
|
|
||||||
bool force_null_render = false;
|
|
||||||
bool force_single_core = false;
|
|
||||||
std::string nickname{};
|
|
||||||
std::string password{};
|
|
||||||
std::string address{};
|
|
||||||
std::string input_profile{};
|
|
||||||
std::optional<std::string> log_filter{};
|
|
||||||
u16 port = Network::DefaultRoomPort;
|
|
||||||
|
|
||||||
static struct option long_options[] = {
|
|
||||||
// clang-format off
|
|
||||||
{"debug", no_argument, 0, 'd'},
|
|
||||||
{"config", required_argument, 0, 'c'},
|
|
||||||
{"fullscreen", no_argument, 0, 'f'},
|
|
||||||
{"help", no_argument, 0, 'h'},
|
|
||||||
{"game", required_argument, 0, 'g'},
|
|
||||||
{"multiplayer", required_argument, 0, 'm'},
|
|
||||||
{"program", optional_argument, 0, 'p'},
|
|
||||||
{"user", required_argument, 0, 'u'},
|
|
||||||
{"version", no_argument, 0, 'v'},
|
|
||||||
{"input-profile", no_argument, 0, 'i'},
|
|
||||||
{"null-render", no_argument, 0, 'n'},
|
|
||||||
{"singlecore", no_argument, 0, 's'},
|
|
||||||
{"filter", no_argument, 0, 'x'},
|
|
||||||
{0, 0, 0, 0},
|
|
||||||
// clang-format on
|
|
||||||
};
|
|
||||||
|
|
||||||
while (optind < argc) {
|
|
||||||
int arg = getopt_long(argc, argv, "g:fhvcip::c:u:d:", long_options, &option_index);
|
|
||||||
if (arg != -1) {
|
|
||||||
switch (char(arg)) {
|
|
||||||
case 'd':
|
|
||||||
override_gdb_port = uint16_t(atoi(optarg));
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
config_path = optarg;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
fullscreen = true;
|
|
||||||
LOG_INFO(Frontend, "Starting in fullscreen mode...");
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
PrintHelp(argv[0]);
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
case 'g':
|
|
||||||
filepath = std::string(optarg);
|
|
||||||
break;
|
|
||||||
case 'i': {
|
|
||||||
input_profile = std::string(optarg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'm': {
|
|
||||||
use_multiplayer = true;
|
|
||||||
const std::string str_arg(optarg);
|
|
||||||
// regex to check if the format is nickname:password@ip:port
|
|
||||||
// with optional :password
|
|
||||||
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
|
|
||||||
if (!std::regex_match(str_arg, re)) {
|
|
||||||
std::cout << "Wrong format for option --multiplayer\n";
|
|
||||||
PrintHelp(argv[0]);
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::smatch match;
|
|
||||||
std::regex_search(str_arg, match, re);
|
|
||||||
ASSERT(match.size() == 5);
|
|
||||||
nickname = match[1];
|
|
||||||
password = match[2];
|
|
||||||
address = match[3];
|
|
||||||
if (!match[4].str().empty()) {
|
|
||||||
port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
|
|
||||||
}
|
|
||||||
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
|
|
||||||
if (!std::regex_match(nickname, nickname_re)) {
|
|
||||||
LOG_ERROR(Frontend, "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
if (address.empty()) {
|
|
||||||
LOG_ERROR(Frontend, "Address to room must not be empty");
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'p':
|
|
||||||
program_args = argv[optind];
|
|
||||||
++optind;
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
selected_user = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
PrintVersion();
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
case 'n':
|
|
||||||
force_null_render = true;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
force_single_core = true;
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
log_filter = argv[optind];
|
|
||||||
++optind;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#ifdef _WIN32
|
|
||||||
filepath = Common::UTF16ToUTF8(argv_w[optind]);
|
|
||||||
#else
|
#else
|
||||||
filepath = argv[optind];
|
wchar_t **argv_w = nullptr;
|
||||||
#endif
|
#endif
|
||||||
optind++;
|
Core::LaunchParams lp = Core::ParseLaunchParams(state->system, argc, argv, argv_w);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SdlConfig config{config_path};
|
|
||||||
|
|
||||||
// apply the log_filter setting
|
|
||||||
// the logger was initialized before and doesn't pick up the filter on its own
|
|
||||||
Common::Log::Filter filter;
|
|
||||||
filter.ParseFilterString(log_filter.value_or(Settings::values.log_filter.GetValue()));
|
|
||||||
Common::Log::SetGlobalFilter(filter);
|
|
||||||
|
|
||||||
if (!program_args.empty()) {
|
|
||||||
Settings::values.program_args = program_args;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!input_profile.empty()) {
|
|
||||||
auto& players = Settings::values.players.GetValue();
|
|
||||||
players[0].profile_name = input_profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selected_user.has_value()) {
|
|
||||||
Settings::values.current_user = std::clamp(*selected_user, 0, 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (override_gdb_port.has_value()) {
|
|
||||||
Settings::values.use_gdbstub = true;
|
|
||||||
Settings::values.gdbstub_port = *override_gdb_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force_single_core) {
|
|
||||||
Settings::values.use_multi_core = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force_null_render) {
|
|
||||||
Settings::values.renderer_backend = Settings::RendererBackend::Null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
LocalFree(argv_w);
|
LocalFree(argv_w);
|
||||||
#endif
|
#endif
|
||||||
|
SdlConfig config{lp.config_path};
|
||||||
|
|
||||||
if (filepath.empty()) {
|
Core::ApplyLaunchParams(lp);
|
||||||
|
|
||||||
|
if (lp.filepath.empty()) {
|
||||||
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
|
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
|
||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -386,14 +212,14 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
|||||||
case Settings::RendererBackend::OpenGL_GLSL:
|
case Settings::RendererBackend::OpenGL_GLSL:
|
||||||
case Settings::RendererBackend::OpenGL_GLASM:
|
case Settings::RendererBackend::OpenGL_GLASM:
|
||||||
case Settings::RendererBackend::OpenGL_SPIRV:
|
case Settings::RendererBackend::OpenGL_SPIRV:
|
||||||
state->emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, state->system, fullscreen);
|
state->emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, state->system, lp.fullscreen);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case Settings::RendererBackend::Vulkan:
|
case Settings::RendererBackend::Vulkan:
|
||||||
state->emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, state->system, fullscreen);
|
state->emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, state->system, lp.fullscreen);
|
||||||
break;
|
break;
|
||||||
case Settings::RendererBackend::Null:
|
case Settings::RendererBackend::Null:
|
||||||
state->emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, state->system, fullscreen);
|
state->emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, state->system, lp.fullscreen);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Frontend, "Invalid renderer backend");
|
LOG_CRITICAL(Frontend, "Invalid renderer backend");
|
||||||
@@ -413,12 +239,12 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
|||||||
Service::AM::FrontendAppletParameters load_parameters{
|
Service::AM::FrontendAppletParameters load_parameters{
|
||||||
.applet_id = Service::AM::AppletId::Application,
|
.applet_id = Service::AM::AppletId::Application,
|
||||||
};
|
};
|
||||||
const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, filepath, load_parameters);
|
const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, lp.filepath, load_parameters);
|
||||||
switch (load_result) {
|
switch (load_result) {
|
||||||
case Core::SystemResultStatus::Success:
|
case Core::SystemResultStatus::Success:
|
||||||
break; // Expected case
|
break; // Expected case
|
||||||
case Core::SystemResultStatus::ErrorGetLoader:
|
case Core::SystemResultStatus::ErrorGetLoader:
|
||||||
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
|
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", lp.filepath);
|
||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
case Core::SystemResultStatus::ErrorLoader:
|
case Core::SystemResultStatus::ErrorLoader:
|
||||||
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
||||||
@@ -440,14 +266,14 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
|||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_multiplayer) {
|
if (lp.use_multiplayer) {
|
||||||
if (auto member = Network::GetRoomMember().lock()) {
|
if (auto member = Network::GetRoomMember().lock()) {
|
||||||
member->BindOnChatMessageReceived(OnMessageReceived);
|
member->BindOnChatMessageReceived(OnMessageReceived);
|
||||||
member->BindOnStatusMessageReceived(OnStatusMessageReceived);
|
member->BindOnStatusMessageReceived(OnStatusMessageReceived);
|
||||||
member->BindOnStateChanged(OnStateChanged);
|
member->BindOnStateChanged(OnStateChanged);
|
||||||
member->BindOnError(OnNetworkError);
|
member->BindOnError(OnNetworkError);
|
||||||
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, nickname);
|
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", lp.address, lp.port, lp.nickname);
|
||||||
member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password);
|
member->Join(lp.nickname, lp.address.c_str(), lp.port, 0, Network::NoPreferredIP, lp.password);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(Network, "Could not access RoomMember");
|
LOG_ERROR(Network, "Could not access RoomMember");
|
||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
|
|||||||
Reference in New Issue
Block a user