Compare commits
46 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8832446730 | ||
|
|
5da72a891f | ||
|
|
f8a7d6a0ae | ||
|
|
da31326c17 | ||
|
|
45672d43e3 | ||
|
|
a3bac5550d | ||
|
|
4faea2bbf4 | ||
|
|
fa10374d39 | ||
|
|
98135dee16 | ||
|
|
7bf4bec257 | ||
|
|
f7d95d0a3a | ||
|
|
bbb202ceed | ||
|
|
789da737af | ||
|
|
b541a35e27 | ||
|
|
6bc1a477bf | ||
|
|
9bfd4d880e | ||
|
|
6a56f42f5d | ||
|
|
b81caf1879 | ||
|
|
b1d633532f | ||
|
|
12c0f682e6 | ||
|
|
9ff891ce71 | ||
|
|
82d80869fc | ||
|
|
20cbf6f3db | ||
|
|
dca4f0687a | ||
|
|
b8c03411e7 | ||
|
|
3ff7a5de1a | ||
|
|
4447c9a46e | ||
|
|
e2f32e8c88 | ||
|
|
beba9c9b61 | ||
|
|
a222f02c7a | ||
|
|
0104e28fe4 | ||
|
|
c5f519e1e4 | ||
|
|
5a5bb91f40 | ||
|
|
4fce72c902 | ||
|
|
77b0d01639 | ||
|
|
18bdf45868 | ||
|
|
09e3029c11 | ||
|
|
2221afaf26 | ||
|
|
45fcde817e | ||
|
|
29fbce9fe6 | ||
|
|
5754456292 | ||
|
|
3cc3176ad6 | ||
|
|
d5f53da79d | ||
|
|
a4696285af | ||
|
|
f6868ae4dd | ||
|
|
0ed80c9818 |
4
externals/find-modules/FindOpus.cmake
vendored
4
externals/find-modules/FindOpus.cmake
vendored
@@ -2,9 +2,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_search_module(OPUS QUIET IMPORTED_TARGET opus)
|
||||
endif()
|
||||
pkg_search_module(OPUS QUIET IMPORTED_TARGET opus)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Opus
|
||||
|
||||
4
externals/find-modules/Findenet.cmake
vendored
4
externals/find-modules/Findenet.cmake
vendored
@@ -3,9 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_search_module(ENET QUIET IMPORTED_TARGET libenet)
|
||||
endif()
|
||||
pkg_search_module(ENET QUIET IMPORTED_TARGET libenet)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(enet
|
||||
|
||||
4
externals/find-modules/Findhttplib.cmake
vendored
4
externals/find-modules/Findhttplib.cmake
vendored
@@ -9,9 +9,7 @@ if (httplib_FOUND)
|
||||
find_package_handle_standard_args(httplib CONFIG_MODE)
|
||||
else()
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_search_module(HTTPLIB QUIET IMPORTED_TARGET cpp-httplib)
|
||||
endif()
|
||||
pkg_search_module(HTTPLIB QUIET IMPORTED_TARGET cpp-httplib)
|
||||
find_package_handle_standard_args(httplib
|
||||
REQUIRED_VARS HTTPLIB_INCLUDEDIR
|
||||
VERSION_VAR HTTPLIB_VERSION
|
||||
|
||||
4
externals/find-modules/Findinih.cmake
vendored
4
externals/find-modules/Findinih.cmake
vendored
@@ -3,9 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader)
|
||||
endif()
|
||||
pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(inih
|
||||
|
||||
4
externals/find-modules/Findlibusb.cmake
vendored
4
externals/find-modules/Findlibusb.cmake
vendored
@@ -3,9 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0)
|
||||
endif()
|
||||
pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(libusb
|
||||
|
||||
4
externals/find-modules/Findlz4.cmake
vendored
4
externals/find-modules/Findlz4.cmake
vendored
@@ -8,9 +8,7 @@ if (lz4_FOUND)
|
||||
find_package_handle_standard_args(lz4 CONFIG_MODE)
|
||||
else()
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4)
|
||||
endif()
|
||||
pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4)
|
||||
find_package_handle_standard_args(lz4
|
||||
REQUIRED_VARS LZ4_LINK_LIBRARIES
|
||||
VERSION_VAR LZ4_VERSION
|
||||
|
||||
4
externals/find-modules/Findzstd.cmake
vendored
4
externals/find-modules/Findzstd.cmake
vendored
@@ -8,9 +8,7 @@ if (zstd_FOUND)
|
||||
find_package_handle_standard_args(zstd CONFIG_MODE)
|
||||
else()
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd)
|
||||
endif()
|
||||
pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd)
|
||||
find_package_handle_standard_args(zstd
|
||||
REQUIRED_VARS ZSTD_LINK_LIBRARIES
|
||||
VERSION_VAR ZSTD_VERSION
|
||||
|
||||
@@ -91,9 +91,10 @@ public:
|
||||
* @param core_timing - The CoreTiming instance
|
||||
* @param session - The device session
|
||||
*
|
||||
* @return Is the buffer was released.
|
||||
* @return If any buffer was released.
|
||||
*/
|
||||
bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session) {
|
||||
bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session,
|
||||
bool force) {
|
||||
std::scoped_lock l{lock};
|
||||
bool buffer_released{false};
|
||||
while (registered_count > 0) {
|
||||
@@ -103,7 +104,8 @@ public:
|
||||
}
|
||||
|
||||
// Check with the backend if this buffer can be released yet.
|
||||
if (!session.IsBufferConsumed(buffers[index])) {
|
||||
// If we're shutting down, we don't care if it's been played or not.
|
||||
if (!force && !session.IsBufferConsumed(buffers[index])) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,12 @@ void DeviceSession::Stop() {
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSession::ClearBuffers() {
|
||||
if (stream) {
|
||||
stream->ClearQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
|
||||
for (const auto& buffer : buffers) {
|
||||
Sink::SinkBuffer new_buffer{
|
||||
|
||||
@@ -90,6 +90,11 @@ public:
|
||||
*/
|
||||
void Stop();
|
||||
|
||||
/**
|
||||
* Clear out the underlying audio buffers in the backend stream.
|
||||
*/
|
||||
void ClearBuffers();
|
||||
|
||||
/**
|
||||
* Set this device session's volume.
|
||||
*
|
||||
|
||||
@@ -23,7 +23,6 @@ System::~System() {
|
||||
void System::Finalize() {
|
||||
Stop();
|
||||
session->Finalize();
|
||||
buffer_event->Signal();
|
||||
}
|
||||
|
||||
void System::StartSession() {
|
||||
@@ -102,6 +101,10 @@ Result System::Stop() {
|
||||
if (state == State::Started) {
|
||||
session->Stop();
|
||||
session->SetVolume(0.0f);
|
||||
session->ClearBuffers();
|
||||
if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) {
|
||||
buffer_event->Signal();
|
||||
}
|
||||
state = State::Stopped;
|
||||
}
|
||||
|
||||
@@ -138,7 +141,7 @@ void System::RegisterBuffers() {
|
||||
}
|
||||
|
||||
void System::ReleaseBuffers() {
|
||||
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)};
|
||||
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)};
|
||||
|
||||
if (signal) {
|
||||
// Signal if any buffer was released, or if none are registered, we need more.
|
||||
|
||||
@@ -24,7 +24,6 @@ System::~System() {
|
||||
void System::Finalize() {
|
||||
Stop();
|
||||
session->Finalize();
|
||||
buffer_event->Signal();
|
||||
}
|
||||
|
||||
std::string_view System::GetDefaultOutputDeviceName() const {
|
||||
@@ -102,6 +101,10 @@ Result System::Stop() {
|
||||
if (state == State::Started) {
|
||||
session->Stop();
|
||||
session->SetVolume(0.0f);
|
||||
session->ClearBuffers();
|
||||
if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) {
|
||||
buffer_event->Signal();
|
||||
}
|
||||
state = State::Stopped;
|
||||
}
|
||||
|
||||
@@ -138,7 +141,7 @@ void System::RegisterBuffers() {
|
||||
}
|
||||
|
||||
void System::ReleaseBuffers() {
|
||||
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)};
|
||||
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)};
|
||||
if (signal) {
|
||||
// Signal if any buffer was released, or if none are registered, we need more.
|
||||
buffer_event->Signal();
|
||||
|
||||
@@ -40,6 +40,7 @@ void LogSettings() {
|
||||
LOG_INFO(Config, "yuzu Configuration:");
|
||||
log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());
|
||||
log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
|
||||
log_setting("System_DeviceName", values.device_name.GetValue());
|
||||
log_setting("System_CurrentUser", values.current_user.GetValue());
|
||||
log_setting("System_LanguageIndex", values.language_index.GetValue());
|
||||
log_setting("System_RegionIndex", values.region_index.GetValue());
|
||||
|
||||
@@ -458,6 +458,7 @@ struct Values {
|
||||
|
||||
// System
|
||||
SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"};
|
||||
Setting<std::string> device_name{"Yuzu", "device_name"};
|
||||
// Measured in seconds since epoch
|
||||
std::optional<s64> custom_rtc;
|
||||
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
|
||||
|
||||
@@ -131,6 +131,10 @@ public:
|
||||
return active_config;
|
||||
}
|
||||
|
||||
bool StrictContextRequired() const {
|
||||
return strict_context_required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the internal configuration to be replaced by the specified argument at some point in
|
||||
* the future.
|
||||
@@ -207,6 +211,8 @@ protected:
|
||||
|
||||
WindowSystemInfo window_info;
|
||||
|
||||
bool strict_context_required = false;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Handler called when the minimal client area was requested to be changed via SetConfig.
|
||||
|
||||
@@ -167,6 +167,9 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
|
||||
}
|
||||
if (incoming) {
|
||||
// Populate the object lists with the data in the IPC request.
|
||||
incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy);
|
||||
incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move);
|
||||
|
||||
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
|
||||
incoming_copy_handles.push_back(rp.Pop<Handle>());
|
||||
}
|
||||
@@ -181,6 +184,11 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
|
||||
}
|
||||
}
|
||||
|
||||
buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors);
|
||||
buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors);
|
||||
buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors);
|
||||
buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors);
|
||||
|
||||
for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
|
||||
buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
|
||||
}
|
||||
@@ -318,25 +326,23 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
|
||||
}
|
||||
|
||||
std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
|
||||
std::vector<u8> buffer{};
|
||||
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
||||
BufferDescriptorA()[buffer_index].Size()};
|
||||
|
||||
if (is_buffer_a) {
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorA().size() > buffer_index, { return buffer; },
|
||||
BufferDescriptorA().size() > buffer_index, { return {}; },
|
||||
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
||||
buffer.resize(BufferDescriptorA()[buffer_index].Size());
|
||||
std::vector<u8> buffer(BufferDescriptorA()[buffer_index].Size());
|
||||
memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
|
||||
return buffer;
|
||||
} else {
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorX().size() > buffer_index, { return buffer; },
|
||||
BufferDescriptorX().size() > buffer_index, { return {}; },
|
||||
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
||||
buffer.resize(BufferDescriptorX()[buffer_index].Size());
|
||||
std::vector<u8> buffer(BufferDescriptorX()[buffer_index].Size());
|
||||
memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
|
||||
|
||||
@@ -285,6 +285,17 @@ void KProcess::UnregisterThread(KThread* thread) {
|
||||
thread_list.remove(thread);
|
||||
}
|
||||
|
||||
u64 KProcess::GetFreeThreadCount() const {
|
||||
if (resource_limit != nullptr) {
|
||||
const auto current_value =
|
||||
resource_limit->GetCurrentValue(LimitableResource::ThreadCountMax);
|
||||
const auto limit_value = resource_limit->GetLimitValue(LimitableResource::ThreadCountMax);
|
||||
return limit_value - current_value;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Result KProcess::Reset() {
|
||||
// Lock the process and the scheduler.
|
||||
KScopedLightLock lk(state_lock);
|
||||
|
||||
@@ -304,6 +304,9 @@ public:
|
||||
/// from this process' thread list.
|
||||
void UnregisterThread(KThread* thread);
|
||||
|
||||
/// Retrieves the number of available threads for this process.
|
||||
u64 GetFreeThreadCount() const;
|
||||
|
||||
/// Clears the signaled state of the process if and only if it's signaled.
|
||||
///
|
||||
/// @pre The process must not be already terminated. If this is called on a
|
||||
|
||||
@@ -784,63 +784,29 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
|
||||
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
|
||||
info_sub_id, handle);
|
||||
|
||||
enum class GetInfoType : u64 {
|
||||
// 1.0.0+
|
||||
AllowedCPUCoreMask = 0,
|
||||
AllowedThreadPriorityMask = 1,
|
||||
MapRegionBaseAddr = 2,
|
||||
MapRegionSize = 3,
|
||||
HeapRegionBaseAddr = 4,
|
||||
HeapRegionSize = 5,
|
||||
TotalPhysicalMemoryAvailable = 6,
|
||||
TotalPhysicalMemoryUsed = 7,
|
||||
IsCurrentProcessBeingDebugged = 8,
|
||||
RegisterResourceLimit = 9,
|
||||
IdleTickCount = 10,
|
||||
RandomEntropy = 11,
|
||||
ThreadTickCount = 0xF0000002,
|
||||
// 2.0.0+
|
||||
ASLRRegionBaseAddr = 12,
|
||||
ASLRRegionSize = 13,
|
||||
StackRegionBaseAddr = 14,
|
||||
StackRegionSize = 15,
|
||||
// 3.0.0+
|
||||
SystemResourceSize = 16,
|
||||
SystemResourceUsage = 17,
|
||||
TitleId = 18,
|
||||
// 4.0.0+
|
||||
PrivilegedProcessId = 19,
|
||||
// 5.0.0+
|
||||
UserExceptionContextAddr = 20,
|
||||
// 6.0.0+
|
||||
TotalPhysicalMemoryAvailableWithoutSystemResource = 21,
|
||||
TotalPhysicalMemoryUsedWithoutSystemResource = 22,
|
||||
|
||||
// Homebrew only
|
||||
MesosphereCurrentProcess = 65001,
|
||||
};
|
||||
|
||||
const auto info_id_type = static_cast<GetInfoType>(info_id);
|
||||
const auto info_id_type = static_cast<InfoType>(info_id);
|
||||
|
||||
switch (info_id_type) {
|
||||
case GetInfoType::AllowedCPUCoreMask:
|
||||
case GetInfoType::AllowedThreadPriorityMask:
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
case GetInfoType::MapRegionSize:
|
||||
case GetInfoType::HeapRegionBaseAddr:
|
||||
case GetInfoType::HeapRegionSize:
|
||||
case GetInfoType::ASLRRegionBaseAddr:
|
||||
case GetInfoType::ASLRRegionSize:
|
||||
case GetInfoType::StackRegionBaseAddr:
|
||||
case GetInfoType::StackRegionSize:
|
||||
case GetInfoType::TotalPhysicalMemoryAvailable:
|
||||
case GetInfoType::TotalPhysicalMemoryUsed:
|
||||
case GetInfoType::SystemResourceSize:
|
||||
case GetInfoType::SystemResourceUsage:
|
||||
case GetInfoType::TitleId:
|
||||
case GetInfoType::UserExceptionContextAddr:
|
||||
case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
|
||||
case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: {
|
||||
case InfoType::CoreMask:
|
||||
case InfoType::PriorityMask:
|
||||
case InfoType::AliasRegionAddress:
|
||||
case InfoType::AliasRegionSize:
|
||||
case InfoType::HeapRegionAddress:
|
||||
case InfoType::HeapRegionSize:
|
||||
case InfoType::AslrRegionAddress:
|
||||
case InfoType::AslrRegionSize:
|
||||
case InfoType::StackRegionAddress:
|
||||
case InfoType::StackRegionSize:
|
||||
case InfoType::TotalMemorySize:
|
||||
case InfoType::UsedMemorySize:
|
||||
case InfoType::SystemResourceSizeTotal:
|
||||
case InfoType::SystemResourceSizeUsed:
|
||||
case InfoType::ProgramId:
|
||||
case InfoType::UserExceptionContextAddress:
|
||||
case InfoType::TotalNonSystemMemorySize:
|
||||
case InfoType::UsedNonSystemMemorySize:
|
||||
case InfoType::IsApplication:
|
||||
case InfoType::FreeThreadCount: {
|
||||
if (info_sub_id != 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
|
||||
info_sub_id);
|
||||
@@ -856,79 +822,83 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
|
||||
}
|
||||
|
||||
switch (info_id_type) {
|
||||
case GetInfoType::AllowedCPUCoreMask:
|
||||
case InfoType::CoreMask:
|
||||
*result = process->GetCoreMask();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::AllowedThreadPriorityMask:
|
||||
case InfoType::PriorityMask:
|
||||
*result = process->GetPriorityMask();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
case InfoType::AliasRegionAddress:
|
||||
*result = process->PageTable().GetAliasRegionStart();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::MapRegionSize:
|
||||
case InfoType::AliasRegionSize:
|
||||
*result = process->PageTable().GetAliasRegionSize();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::HeapRegionBaseAddr:
|
||||
case InfoType::HeapRegionAddress:
|
||||
*result = process->PageTable().GetHeapRegionStart();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::HeapRegionSize:
|
||||
case InfoType::HeapRegionSize:
|
||||
*result = process->PageTable().GetHeapRegionSize();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::ASLRRegionBaseAddr:
|
||||
case InfoType::AslrRegionAddress:
|
||||
*result = process->PageTable().GetAliasCodeRegionStart();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::ASLRRegionSize:
|
||||
case InfoType::AslrRegionSize:
|
||||
*result = process->PageTable().GetAliasCodeRegionSize();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::StackRegionBaseAddr:
|
||||
case InfoType::StackRegionAddress:
|
||||
*result = process->PageTable().GetStackRegionStart();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::StackRegionSize:
|
||||
case InfoType::StackRegionSize:
|
||||
*result = process->PageTable().GetStackRegionSize();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryAvailable:
|
||||
case InfoType::TotalMemorySize:
|
||||
*result = process->GetTotalPhysicalMemoryAvailable();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryUsed:
|
||||
case InfoType::UsedMemorySize:
|
||||
*result = process->GetTotalPhysicalMemoryUsed();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::SystemResourceSize:
|
||||
case InfoType::SystemResourceSizeTotal:
|
||||
*result = process->GetSystemResourceSize();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::SystemResourceUsage:
|
||||
case InfoType::SystemResourceSizeUsed:
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
|
||||
*result = process->GetSystemResourceUsage();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::TitleId:
|
||||
case InfoType::ProgramId:
|
||||
*result = process->GetProgramID();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::UserExceptionContextAddr:
|
||||
case InfoType::UserExceptionContextAddress:
|
||||
*result = process->GetProcessLocalRegionAddress();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
|
||||
case InfoType::TotalNonSystemMemorySize:
|
||||
*result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource:
|
||||
case InfoType::UsedNonSystemMemorySize:
|
||||
*result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
|
||||
return ResultSuccess;
|
||||
|
||||
case InfoType::FreeThreadCount:
|
||||
*result = process->GetFreeThreadCount();
|
||||
return ResultSuccess;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -937,11 +907,11 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
|
||||
return ResultInvalidEnumValue;
|
||||
}
|
||||
|
||||
case GetInfoType::IsCurrentProcessBeingDebugged:
|
||||
case InfoType::DebuggerAttached:
|
||||
*result = 0;
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::RegisterResourceLimit: {
|
||||
case InfoType::ResourceLimit: {
|
||||
if (handle != 0) {
|
||||
LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
|
||||
return ResultInvalidHandle;
|
||||
@@ -969,7 +939,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
case GetInfoType::RandomEntropy:
|
||||
case InfoType::RandomEntropy:
|
||||
if (handle != 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
|
||||
handle);
|
||||
@@ -985,13 +955,13 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
|
||||
*result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::PrivilegedProcessId:
|
||||
case InfoType::InitialProcessIdRange:
|
||||
LOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
|
||||
*result = 0;
|
||||
return ResultSuccess;
|
||||
|
||||
case GetInfoType::ThreadTickCount: {
|
||||
case InfoType::ThreadTickCount: {
|
||||
constexpr u64 num_cpus = 4;
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
||||
LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
|
||||
@@ -1026,7 +996,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
|
||||
*result = out_ticks;
|
||||
return ResultSuccess;
|
||||
}
|
||||
case GetInfoType::IdleTickCount: {
|
||||
case InfoType::IdleTickCount: {
|
||||
// Verify the input handle is invalid.
|
||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||
|
||||
@@ -1040,7 +1010,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
|
||||
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
|
||||
return ResultSuccess;
|
||||
}
|
||||
case GetInfoType::MesosphereCurrentProcess: {
|
||||
case InfoType::MesosphereCurrentProcess: {
|
||||
// Verify the input handle is invalid.
|
||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||
|
||||
|
||||
@@ -203,8 +203,9 @@ private:
|
||||
};
|
||||
|
||||
AudInU::AudInU(Core::System& system_)
|
||||
: ServiceFramework{system_, "audin:u"}, service_context{system_, "AudInU"},
|
||||
impl{std::make_unique<AudioCore::AudioIn::Manager>(system_)} {
|
||||
: ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
|
||||
system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudInU::ListAudioIns, "ListAudioIns"},
|
||||
|
||||
@@ -26,8 +26,9 @@ public:
|
||||
explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
|
||||
size_t session_id, const std::string& device_name,
|
||||
const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
|
||||
: ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
|
||||
event{service_context.CreateEvent("AudioOutEvent")},
|
||||
: ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
|
||||
"AudioOutEvent")},
|
||||
impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
|
||||
|
||||
// clang-format off
|
||||
@@ -220,8 +221,9 @@ private:
|
||||
};
|
||||
|
||||
AudOutU::AudOutU(Core::System& system_)
|
||||
: ServiceFramework{system_, "audout:u"}, service_context{system_, "AudOutU"},
|
||||
impl{std::make_unique<AudioCore::AudioOut::Manager>(system_)} {
|
||||
: ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>(
|
||||
system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
|
||||
|
||||
@@ -35,9 +35,10 @@ public:
|
||||
AudioCore::AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
u32 process_handle, u64 applet_resource_user_id, s32 session_id)
|
||||
: ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"},
|
||||
rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_},
|
||||
impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
|
||||
: ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent(
|
||||
"IAudioRendererEvent")},
|
||||
manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
|
||||
@@ -242,8 +243,10 @@ class IAudioDevice final : public ServiceFramework<IAudioDevice> {
|
||||
public:
|
||||
explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
|
||||
u32 device_num)
|
||||
: ServiceFramework{system_, "IAudioDevice"}, service_context{system_, "IAudioDevice"},
|
||||
impl{std::make_unique<AudioDevice>(system_, applet_resource_user_id, revision)},
|
||||
: ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>(
|
||||
system_, applet_resource_user_id,
|
||||
revision)},
|
||||
event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
|
||||
@@ -418,7 +421,7 @@ private:
|
||||
};
|
||||
|
||||
AudRenU::AudRenU(Core::System& system_)
|
||||
: ServiceFramework{system_, "audren:u"},
|
||||
: ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
|
||||
@@ -191,6 +191,13 @@ void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) {
|
||||
GetKeyCodeMapImpl(ctx);
|
||||
}
|
||||
|
||||
void SET::GetDeviceNickName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
ctx.WriteBuffer(Settings::values.device_name.GetValue());
|
||||
}
|
||||
|
||||
SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -205,7 +212,7 @@ SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} {
|
||||
{8, &SET::GetQuestFlag, "GetQuestFlag"},
|
||||
{9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},
|
||||
{10, nullptr, "GetFirmwareVersionForDebug"},
|
||||
{11, nullptr, "GetDeviceNickName"},
|
||||
{11, &SET::GetDeviceNickName, "GetDeviceNickName"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ private:
|
||||
void GetRegionCode(Kernel::HLERequestContext& ctx);
|
||||
void GetKeyCodeMap(Kernel::HLERequestContext& ctx);
|
||||
void GetKeyCodeMap2(Kernel::HLERequestContext& ctx);
|
||||
void GetDeviceNickName(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Set
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/system_archive/system_version.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -176,6 +177,13 @@ void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(response);
|
||||
}
|
||||
|
||||
void SET_SYS::GetDeviceNickName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
ctx.WriteBuffer(::Settings::values.device_name.GetValue());
|
||||
}
|
||||
|
||||
SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -253,7 +261,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
|
||||
{74, nullptr, "SetWirelessLanEnableFlag"},
|
||||
{75, nullptr, "GetInitialLaunchSettings"},
|
||||
{76, nullptr, "SetInitialLaunchSettings"},
|
||||
{77, nullptr, "GetDeviceNickName"},
|
||||
{77, &SET_SYS::GetDeviceNickName, "GetDeviceNickName"},
|
||||
{78, nullptr, "SetDeviceNickName"},
|
||||
{79, nullptr, "GetProductModel"},
|
||||
{80, nullptr, "GetLdnChannel"},
|
||||
|
||||
@@ -29,6 +29,7 @@ private:
|
||||
void GetFirmwareVersion2(Kernel::HLERequestContext& ctx);
|
||||
void GetColorSetId(Kernel::HLERequestContext& ctx);
|
||||
void SetColorSetId(Kernel::HLERequestContext& ctx);
|
||||
void GetDeviceNickName(Kernel::HLERequestContext& ctx);
|
||||
|
||||
ColorSet color_set = ColorSet::BasicWhite;
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)
|
||||
PreSetController(identifier);
|
||||
}
|
||||
|
||||
void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) {
|
||||
void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) {
|
||||
const std::size_t desired_width = getImageWidth();
|
||||
const std::size_t desired_height = getImageHeight();
|
||||
status.data.resize(desired_width * desired_height);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
@@ -15,7 +17,7 @@ class Camera final : public InputEngine {
|
||||
public:
|
||||
explicit Camera(std::string input_engine_);
|
||||
|
||||
void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data);
|
||||
void SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data);
|
||||
|
||||
std::size_t getImageWidth() const;
|
||||
std::size_t getImageHeight() const;
|
||||
|
||||
@@ -461,7 +461,7 @@ void EmitSetSampleMask(EmitContext& ctx, Id value) {
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, Id value) {
|
||||
if (!ctx.runtime_info.convert_depth_mode) {
|
||||
if (!ctx.runtime_info.convert_depth_mode || ctx.profile.support_native_ndc) {
|
||||
ctx.OpStore(ctx.frag_depth, value);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,8 @@ void EmitPrologue(EmitContext& ctx) {
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext& ctx) {
|
||||
if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) {
|
||||
if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode &&
|
||||
!ctx.profile.support_native_ndc) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
@@ -125,7 +126,7 @@ void EmitEpilogue(EmitContext& ctx) {
|
||||
}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (ctx.runtime_info.convert_depth_mode) {
|
||||
if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (stream.IsImmediate()) {
|
||||
|
||||
@@ -1345,8 +1345,10 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
||||
(profile.warp_size_potentially_larger_than_guest &&
|
||||
(info.uses_subgroup_vote || info.uses_subgroup_mask))) {
|
||||
AddCapability(spv::Capability::GroupNonUniform);
|
||||
subgroup_local_invocation_id =
|
||||
DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
|
||||
Decorate(subgroup_local_invocation_id, spv::Decoration::Flat);
|
||||
}
|
||||
if (info.uses_fswzadd) {
|
||||
const Id f32_one{Const(1.0f)};
|
||||
|
||||
@@ -35,6 +35,7 @@ struct Profile {
|
||||
bool support_int64_atomics{};
|
||||
bool support_derivative_control{};
|
||||
bool support_geometry_shader_passthrough{};
|
||||
bool support_native_ndc{};
|
||||
bool support_gl_nv_gpu_shader_5{};
|
||||
bool support_gl_amd_gpu_shader_half_float{};
|
||||
bool support_gl_texture_shadow_lod{};
|
||||
|
||||
@@ -223,8 +223,6 @@ struct GPU::Impl {
|
||||
/// core timing events.
|
||||
void Start() {
|
||||
gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
|
||||
cpu_context = renderer->GetRenderWindow().CreateSharedContext();
|
||||
cpu_context->MakeCurrent();
|
||||
}
|
||||
|
||||
void NotifyShutdown() {
|
||||
@@ -235,6 +233,9 @@ struct GPU::Impl {
|
||||
|
||||
/// Obtain the CPU Context
|
||||
void ObtainContext() {
|
||||
if (!cpu_context) {
|
||||
cpu_context = renderer->GetRenderWindow().CreateSharedContext();
|
||||
}
|
||||
cpu_context->MakeCurrent();
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ bool IsASTCSupported() {
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Device::Device() {
|
||||
Device::Device(Core::Frontend::EmuWindow& emu_window) {
|
||||
if (!GLAD_GL_VERSION_4_6) {
|
||||
LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available");
|
||||
throw std::runtime_error{"Insufficient version"};
|
||||
@@ -126,9 +126,9 @@ Device::Device() {
|
||||
const bool is_intel = vendor_name == "Intel";
|
||||
|
||||
#ifdef __unix__
|
||||
const bool is_linux = true;
|
||||
constexpr bool is_linux = true;
|
||||
#else
|
||||
const bool is_linux = false;
|
||||
constexpr bool is_linux = false;
|
||||
#endif
|
||||
|
||||
bool disable_fast_buffer_sub_data = false;
|
||||
@@ -193,9 +193,11 @@ Device::Device() {
|
||||
}
|
||||
}
|
||||
|
||||
strict_context_required = emu_window.StrictContextRequired();
|
||||
// Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation.
|
||||
// Blocks EGL on Wayland from using asynchronous shader compilation.
|
||||
use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() &&
|
||||
!(is_amd || (is_intel && !is_linux));
|
||||
!(is_amd || (is_intel && !is_linux)) && !strict_context_required;
|
||||
use_driver_cache = is_nvidia;
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Settings {
|
||||
@@ -15,7 +16,7 @@ namespace OpenGL {
|
||||
|
||||
class Device {
|
||||
public:
|
||||
explicit Device();
|
||||
explicit Device(Core::Frontend::EmuWindow& emu_window);
|
||||
|
||||
[[nodiscard]] std::string GetVendorName() const;
|
||||
|
||||
@@ -173,6 +174,10 @@ public:
|
||||
return can_report_memory;
|
||||
}
|
||||
|
||||
bool StrictContextRequired() const {
|
||||
return strict_context_required;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool TestVariableAoffi();
|
||||
static bool TestPreciseBug();
|
||||
@@ -216,6 +221,7 @@ private:
|
||||
bool has_cbuf_ftou_bug{};
|
||||
bool has_bool_ref_bug{};
|
||||
bool can_report_memory{};
|
||||
bool strict_context_required{};
|
||||
|
||||
std::string vendor_name;
|
||||
};
|
||||
|
||||
@@ -174,6 +174,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
|
||||
texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_},
|
||||
state_tracker{state_tracker_}, shader_notify{shader_notify_},
|
||||
use_asynchronous_shaders{device.UseAsynchronousShaders()},
|
||||
strict_context_required{device.StrictContextRequired()},
|
||||
profile{
|
||||
.supported_spirv = 0x00010000,
|
||||
|
||||
@@ -203,6 +204,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
|
||||
.support_int64_atomics = false,
|
||||
.support_derivative_control = device.HasDerivativeControl(),
|
||||
.support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
|
||||
.support_native_ndc = true,
|
||||
.support_gl_nv_gpu_shader_5 = device.HasNvGpuShader5(),
|
||||
.support_gl_amd_gpu_shader_half_float = device.HasAmdShaderHalfFloat(),
|
||||
.support_gl_texture_shadow_lod = device.HasTextureShadowLod(),
|
||||
@@ -255,9 +257,14 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||
}
|
||||
shader_cache_filename = base_dir / "opengl.bin";
|
||||
|
||||
if (!workers) {
|
||||
if (!workers && !strict_context_required) {
|
||||
workers = CreateWorkers();
|
||||
}
|
||||
std::optional<Context> strict_context;
|
||||
if (strict_context_required) {
|
||||
strict_context.emplace(emu_window);
|
||||
}
|
||||
|
||||
struct {
|
||||
std::mutex mutex;
|
||||
size_t total{};
|
||||
@@ -265,44 +272,49 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||
bool has_loaded{};
|
||||
} state;
|
||||
|
||||
const auto queue_work{[&](Common::UniqueFunction<void, Context*>&& work) {
|
||||
if (strict_context_required) {
|
||||
work(&strict_context.value());
|
||||
} else {
|
||||
workers->QueueWork(std::move(work));
|
||||
}
|
||||
}};
|
||||
const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
|
||||
ComputePipelineKey key;
|
||||
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
||||
workers->QueueWork(
|
||||
[this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
|
||||
ctx->pools.ReleaseContents();
|
||||
auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
|
||||
std::scoped_lock lock{state.mutex};
|
||||
if (pipeline) {
|
||||
compute_cache.emplace(key, std::move(pipeline));
|
||||
}
|
||||
++state.built;
|
||||
if (state.has_loaded) {
|
||||
callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
|
||||
}
|
||||
});
|
||||
queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
|
||||
ctx->pools.ReleaseContents();
|
||||
auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
|
||||
std::scoped_lock lock{state.mutex};
|
||||
if (pipeline) {
|
||||
compute_cache.emplace(key, std::move(pipeline));
|
||||
}
|
||||
++state.built;
|
||||
if (state.has_loaded) {
|
||||
callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
|
||||
}
|
||||
});
|
||||
++state.total;
|
||||
}};
|
||||
const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) {
|
||||
GraphicsPipelineKey key;
|
||||
file.read(reinterpret_cast<char*>(&key), sizeof(key));
|
||||
workers->QueueWork(
|
||||
[this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable {
|
||||
boost::container::static_vector<Shader::Environment*, 5> env_ptrs;
|
||||
for (auto& env : envs) {
|
||||
env_ptrs.push_back(&env);
|
||||
}
|
||||
ctx->pools.ReleaseContents();
|
||||
auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
|
||||
std::scoped_lock lock{state.mutex};
|
||||
if (pipeline) {
|
||||
graphics_cache.emplace(key, std::move(pipeline));
|
||||
}
|
||||
++state.built;
|
||||
if (state.has_loaded) {
|
||||
callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
|
||||
}
|
||||
});
|
||||
queue_work([this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable {
|
||||
boost::container::static_vector<Shader::Environment*, 5> env_ptrs;
|
||||
for (auto& env : envs) {
|
||||
env_ptrs.push_back(&env);
|
||||
}
|
||||
ctx->pools.ReleaseContents();
|
||||
auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
|
||||
std::scoped_lock lock{state.mutex};
|
||||
if (pipeline) {
|
||||
graphics_cache.emplace(key, std::move(pipeline));
|
||||
}
|
||||
++state.built;
|
||||
if (state.has_loaded) {
|
||||
callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
|
||||
}
|
||||
});
|
||||
++state.total;
|
||||
}};
|
||||
LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics);
|
||||
@@ -314,6 +326,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||
state.has_loaded = true;
|
||||
lock.unlock();
|
||||
|
||||
if (strict_context_required) {
|
||||
return;
|
||||
}
|
||||
workers->WaitForRequests(stop_loading);
|
||||
if (!use_asynchronous_shaders) {
|
||||
workers.reset();
|
||||
|
||||
@@ -69,6 +69,7 @@ private:
|
||||
StateTracker& state_tracker;
|
||||
VideoCore::ShaderNotify& shader_notify;
|
||||
const bool use_asynchronous_shaders;
|
||||
const bool strict_context_required;
|
||||
|
||||
GraphicsPipelineKey graphics_key{};
|
||||
GraphicsPipeline* current_pipeline{};
|
||||
|
||||
@@ -140,8 +140,8 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
|
||||
Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> context_)
|
||||
: RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_},
|
||||
emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{},
|
||||
program_manager{device},
|
||||
emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, device{emu_window_},
|
||||
state_tracker{}, program_manager{device},
|
||||
rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) {
|
||||
if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
|
||||
@@ -139,23 +139,25 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
RenderScreenshot(*framebuffer, use_accelerated);
|
||||
|
||||
bool has_been_recreated = false;
|
||||
const auto recreate_swapchain = [&] {
|
||||
const auto recreate_swapchain = [&](u32 width, u32 height) {
|
||||
if (!has_been_recreated) {
|
||||
has_been_recreated = true;
|
||||
scheduler.Finish();
|
||||
}
|
||||
const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
|
||||
swapchain.Create(layout.width, layout.height, is_srgb);
|
||||
swapchain.Create(width, height, is_srgb);
|
||||
};
|
||||
if (swapchain.NeedsRecreation(is_srgb)) {
|
||||
recreate_swapchain();
|
||||
|
||||
const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
|
||||
if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width ||
|
||||
swapchain.GetHeight() != layout.height) {
|
||||
recreate_swapchain(layout.width, layout.height);
|
||||
}
|
||||
bool is_outdated;
|
||||
do {
|
||||
swapchain.AcquireNextImage();
|
||||
is_outdated = swapchain.IsOutDated();
|
||||
if (is_outdated) {
|
||||
recreate_swapchain();
|
||||
recreate_swapchain(layout.width, layout.height);
|
||||
}
|
||||
} while (is_outdated);
|
||||
if (has_been_recreated) {
|
||||
|
||||
@@ -285,6 +285,9 @@ void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) {
|
||||
|
||||
void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size,
|
||||
u32 stride) {
|
||||
if (index >= device.GetMaxVertexInputBindings()) {
|
||||
return;
|
||||
}
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
|
||||
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
|
||||
|
||||
@@ -529,7 +529,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
|
||||
static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
|
||||
if (key.state.dynamic_vertex_input) {
|
||||
for (size_t index = 0; index < key.state.attributes.size(); ++index) {
|
||||
const size_t num_vertex_arrays = std::min(
|
||||
key.state.attributes.size(), static_cast<size_t>(device.GetMaxVertexInputBindings()));
|
||||
for (size_t index = 0; index < num_vertex_arrays; ++index) {
|
||||
const u32 type = key.state.DynamicAttributeType(index);
|
||||
if (!stage_infos[0].loads.Generic(index) || type == 0) {
|
||||
continue;
|
||||
@@ -551,7 +553,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
const size_t num_vertex_arrays = std::min(
|
||||
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
|
||||
for (size_t index = 0; index < num_vertex_arrays; ++index) {
|
||||
const bool instanced = key.state.binding_divisors[index] != 0;
|
||||
const auto rate =
|
||||
instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
@@ -580,6 +584,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
});
|
||||
}
|
||||
}
|
||||
ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes());
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -634,23 +640,33 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
};
|
||||
std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
|
||||
std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle);
|
||||
const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
|
||||
VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.viewportCount = Maxwell::NumViewports,
|
||||
.pViewportSwizzles = swizzles.data(),
|
||||
};
|
||||
const VkPipelineViewportStateCreateInfo viewport_ci{
|
||||
VkPipelineViewportDepthClipControlCreateInfoEXT ndc_info{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.negativeOneToOne = key.state.ndc_minus_one_to_one.Value() != 0 ? VK_TRUE : VK_FALSE,
|
||||
};
|
||||
VkPipelineViewportStateCreateInfo viewport_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pNext = device.IsNvViewportSwizzleSupported() ? &swizzle_ci : nullptr,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.viewportCount = Maxwell::NumViewports,
|
||||
.pViewports = nullptr,
|
||||
.scissorCount = Maxwell::NumViewports,
|
||||
.pScissors = nullptr,
|
||||
};
|
||||
|
||||
if (device.IsNvViewportSwizzleSupported()) {
|
||||
swizzle_ci.pNext = std::exchange(viewport_ci.pNext, &swizzle_ci);
|
||||
}
|
||||
if (device.IsExtDepthClipControlSupported()) {
|
||||
ndc_info.pNext = std::exchange(viewport_ci.pNext, &ndc_info);
|
||||
}
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
|
||||
@@ -321,6 +321,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
|
||||
.support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
|
||||
.support_derivative_control = true,
|
||||
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
|
||||
.support_native_ndc = device.IsExtDepthClipControlSupported(),
|
||||
|
||||
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
|
||||
|
||||
@@ -341,6 +342,15 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
|
||||
.support_snorm_render_buffer = true,
|
||||
.support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
|
||||
};
|
||||
|
||||
if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) {
|
||||
LOG_WARNING(Render_Vulkan, "maxVertexInputAttributes is too low: {} < {}",
|
||||
device.GetMaxVertexInputAttributes(), Maxwell::NumVertexAttributes);
|
||||
}
|
||||
if (device.GetMaxVertexInputBindings() < Maxwell::NumVertexArrays) {
|
||||
LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}",
|
||||
device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
|
||||
}
|
||||
}
|
||||
|
||||
PipelineCache::~PipelineCache() = default;
|
||||
|
||||
@@ -658,8 +658,7 @@ void RasterizerVulkan::BeginTransformFeedback() {
|
||||
return;
|
||||
}
|
||||
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) ||
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry));
|
||||
regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation));
|
||||
scheduler.Record(
|
||||
[](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); });
|
||||
}
|
||||
|
||||
@@ -67,17 +67,19 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, u32 width,
|
||||
u32 height, bool srgb)
|
||||
Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
|
||||
u32 width_, u32 height_, bool srgb)
|
||||
: surface{surface_}, device{device_}, scheduler{scheduler_} {
|
||||
Create(width, height, srgb);
|
||||
Create(width_, height_, srgb);
|
||||
}
|
||||
|
||||
Swapchain::~Swapchain() = default;
|
||||
|
||||
void Swapchain::Create(u32 width, u32 height, bool srgb) {
|
||||
void Swapchain::Create(u32 width_, u32 height_, bool srgb) {
|
||||
is_outdated = false;
|
||||
is_suboptimal = false;
|
||||
width = width_;
|
||||
height = height_;
|
||||
|
||||
const auto physical_device = device.GetPhysical();
|
||||
const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)};
|
||||
@@ -88,7 +90,7 @@ void Swapchain::Create(u32 width, u32 height, bool srgb) {
|
||||
device.GetLogical().WaitIdle();
|
||||
Destroy();
|
||||
|
||||
CreateSwapchain(capabilities, width, height, srgb);
|
||||
CreateSwapchain(capabilities, srgb);
|
||||
CreateSemaphores();
|
||||
CreateImageViews();
|
||||
|
||||
@@ -148,8 +150,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
|
||||
}
|
||||
}
|
||||
|
||||
void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height,
|
||||
bool srgb) {
|
||||
void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) {
|
||||
const auto physical_device{device.GetPhysical()};
|
||||
const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
|
||||
const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
|
||||
|
||||
@@ -80,9 +80,16 @@ public:
|
||||
return *present_semaphores[frame_index];
|
||||
}
|
||||
|
||||
u32 GetWidth() const {
|
||||
return width;
|
||||
}
|
||||
|
||||
u32 GetHeight() const {
|
||||
return height;
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height,
|
||||
bool srgb);
|
||||
void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb);
|
||||
void CreateSemaphores();
|
||||
void CreateImageViews();
|
||||
|
||||
@@ -105,6 +112,9 @@ private:
|
||||
std::vector<u64> resource_ticks;
|
||||
std::vector<vk::Semaphore> present_semaphores;
|
||||
|
||||
u32 width;
|
||||
u32 height;
|
||||
|
||||
u32 image_index{};
|
||||
u32 frame_index{};
|
||||
|
||||
|
||||
@@ -421,7 +421,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
VkPhysicalDevice8BitStorageFeatures bit8_storage{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES,
|
||||
.pNext = nullptr,
|
||||
.storageBuffer8BitAccess = false,
|
||||
.storageBuffer8BitAccess = true,
|
||||
.uniformAndStorageBuffer8BitAccess = true,
|
||||
.storagePushConstant8 = false,
|
||||
};
|
||||
@@ -660,6 +660,16 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
|
||||
}
|
||||
|
||||
VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features;
|
||||
if (ext_depth_clip_control) {
|
||||
depth_clip_control_features = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT,
|
||||
.pNext = nullptr,
|
||||
.depthClipControl = VK_TRUE,
|
||||
};
|
||||
SetNext(next, depth_clip_control_features);
|
||||
}
|
||||
|
||||
VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
|
||||
if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) {
|
||||
nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>();
|
||||
@@ -1044,6 +1054,7 @@ void Device::CheckSuitability(bool requires_swapchain) const {
|
||||
std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"),
|
||||
std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess,
|
||||
"uniformAndStorageBuffer16BitAccess"),
|
||||
std::make_pair(bit8_storage.storageBuffer8BitAccess, "storageBuffer8BitAccess"),
|
||||
std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess,
|
||||
"uniformAndStorageBuffer8BitAccess"),
|
||||
std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"),
|
||||
@@ -1083,6 +1094,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
bool has_ext_vertex_input_dynamic_state{};
|
||||
bool has_ext_line_rasterization{};
|
||||
bool has_ext_primitive_topology_list_restart{};
|
||||
bool has_ext_depth_clip_control{};
|
||||
for (const std::string& extension : supported_extensions) {
|
||||
const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
|
||||
bool push) {
|
||||
@@ -1116,6 +1128,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true);
|
||||
test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,
|
||||
true);
|
||||
test(has_ext_depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, false);
|
||||
test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
|
||||
test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
|
||||
test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
|
||||
@@ -1279,6 +1292,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
|
||||
ext_line_rasterization = true;
|
||||
}
|
||||
}
|
||||
if (has_ext_depth_clip_control) {
|
||||
VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features;
|
||||
depth_clip_control_features.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT;
|
||||
depth_clip_control_features.pNext = nullptr;
|
||||
features.pNext = &depth_clip_control_features;
|
||||
physical.GetFeatures2(features);
|
||||
|
||||
if (depth_clip_control_features.depthClipControl) {
|
||||
extensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
|
||||
ext_depth_clip_control = true;
|
||||
}
|
||||
}
|
||||
if (has_khr_workgroup_memory_explicit_layout) {
|
||||
VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout;
|
||||
layout.sType =
|
||||
@@ -1380,6 +1406,10 @@ void Device::SetupFeatures() {
|
||||
is_shader_storage_image_multisample = features.shaderStorageImageMultisample;
|
||||
is_blit_depth_stencil_supported = TestDepthStencilBlits();
|
||||
is_optimal_astc_supported = IsOptimalAstcSupported(features);
|
||||
|
||||
const VkPhysicalDeviceLimits& limits{properties.limits};
|
||||
max_vertex_input_attributes = limits.maxVertexInputAttributes;
|
||||
max_vertex_input_bindings = limits.maxVertexInputBindings;
|
||||
}
|
||||
|
||||
void Device::SetupProperties() {
|
||||
|
||||
@@ -256,6 +256,11 @@ public:
|
||||
return ext_depth_range_unrestricted;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_depth_clip_control.
|
||||
bool IsExtDepthClipControlSupported() const {
|
||||
return ext_depth_clip_control;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_shader_viewport_index_layer.
|
||||
bool IsExtShaderViewportIndexLayerSupported() const {
|
||||
return ext_shader_viewport_index_layer;
|
||||
@@ -368,6 +373,14 @@ public:
|
||||
return must_emulate_bgr565;
|
||||
}
|
||||
|
||||
u32 GetMaxVertexInputAttributes() const {
|
||||
return max_vertex_input_attributes;
|
||||
}
|
||||
|
||||
u32 GetMaxVertexInputBindings() const {
|
||||
return max_vertex_input_bindings;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Checks if the physical device is suitable.
|
||||
void CheckSuitability(bool requires_swapchain) const;
|
||||
@@ -446,6 +459,7 @@ private:
|
||||
bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format.
|
||||
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
|
||||
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
|
||||
bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control
|
||||
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
|
||||
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
|
||||
bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
|
||||
@@ -467,6 +481,8 @@ private:
|
||||
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
|
||||
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
|
||||
bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
|
||||
u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline
|
||||
u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline
|
||||
|
||||
// Telemetry parameters
|
||||
std::string vendor_name; ///< Device's driver name.
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
#include "yuzu/bootmanager.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
static Core::Frontend::WindowSystemType GetWindowSystemType();
|
||||
|
||||
EmuThread::EmuThread(Core::System& system_) : system{system_} {}
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
@@ -61,8 +63,6 @@ void EmuThread::run() {
|
||||
|
||||
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU
|
||||
// execution.
|
||||
gpu.Start();
|
||||
|
||||
gpu.ObtainContext();
|
||||
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
@@ -77,6 +77,7 @@ void EmuThread::run() {
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||
|
||||
gpu.ReleaseContext();
|
||||
gpu.Start();
|
||||
|
||||
system.GetCpuManager().OnGpuReady();
|
||||
|
||||
@@ -230,6 +231,9 @@ public:
|
||||
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
if (GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) {
|
||||
setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~RenderWidget() = default;
|
||||
@@ -274,12 +278,14 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() {
|
||||
return Core::Frontend::WindowSystemType::X11;
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
return Core::Frontend::WindowSystemType::Wayland;
|
||||
else if (platform_name == QStringLiteral("wayland-egl"))
|
||||
return Core::Frontend::WindowSystemType::Wayland;
|
||||
else if (platform_name == QStringLiteral("cocoa"))
|
||||
return Core::Frontend::WindowSystemType::Cocoa;
|
||||
else if (platform_name == QStringLiteral("android"))
|
||||
return Core::Frontend::WindowSystemType::Android;
|
||||
|
||||
LOG_CRITICAL(Frontend, "Unknown Qt platform!");
|
||||
LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString());
|
||||
return Core::Frontend::WindowSystemType::Windows;
|
||||
}
|
||||
|
||||
@@ -319,6 +325,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
||||
input_subsystem->Initialize();
|
||||
this->setMouseTracking(true);
|
||||
|
||||
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") ||
|
||||
QGuiApplication::platformName() == QStringLiteral("wayland-egl");
|
||||
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
|
||||
Qt::QueuedConnection);
|
||||
@@ -755,6 +764,7 @@ void GRenderWindow::InitializeCamera() {
|
||||
return;
|
||||
}
|
||||
|
||||
camera_data.resize(CAMERA_WIDTH * CAMERA_HEIGHT);
|
||||
camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer);
|
||||
connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
|
||||
&GRenderWindow::OnCameraCapture);
|
||||
@@ -810,16 +820,13 @@ void GRenderWindow::RequestCameraCapture() {
|
||||
}
|
||||
|
||||
void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {
|
||||
constexpr std::size_t camera_width = 320;
|
||||
constexpr std::size_t camera_height = 240;
|
||||
// TODO: Capture directly in the format and resolution needed
|
||||
const auto converted =
|
||||
img.scaled(camera_width, camera_height, Qt::AspectRatioMode::IgnoreAspectRatio,
|
||||
img.scaled(CAMERA_WIDTH, CAMERA_HEIGHT, Qt::AspectRatioMode::IgnoreAspectRatio,
|
||||
Qt::TransformationMode::SmoothTransformation)
|
||||
.mirrored(false, true);
|
||||
std::vector<u32> camera_data{};
|
||||
camera_data.resize(camera_width * camera_height);
|
||||
std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32));
|
||||
input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data);
|
||||
std::memcpy(camera_data.data(), converted.bits(), CAMERA_WIDTH * CAMERA_HEIGHT * sizeof(u32));
|
||||
input_subsystem->GetCamera()->SetCameraData(CAMERA_WIDTH, CAMERA_HEIGHT, camera_data);
|
||||
pending_camera_snapshots = 0;
|
||||
}
|
||||
|
||||
@@ -957,6 +964,12 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
|
||||
|
||||
bool GRenderWindow::InitializeOpenGL() {
|
||||
#ifdef HAS_OPENGL
|
||||
if (!QOpenGLContext::supportsThreadedOpenGL()) {
|
||||
QMessageBox::warning(this, tr("OpenGL not available!"),
|
||||
tr("OpenGL shared contexts are not supported."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
auto child = new OpenGLRenderWidget(this);
|
||||
|
||||
@@ -247,6 +247,9 @@ private:
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
|
||||
std::unique_ptr<QCamera> camera;
|
||||
std::unique_ptr<QCameraImageCapture> camera_capture;
|
||||
static constexpr std::size_t CAMERA_WIDTH = 320;
|
||||
static constexpr std::size_t CAMERA_HEIGHT = 240;
|
||||
std::vector<u32> camera_data;
|
||||
#endif
|
||||
std::unique_ptr<QTimer> camera_timer;
|
||||
|
||||
|
||||
@@ -697,7 +697,6 @@ void Config::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.fsr_sharpening_slider);
|
||||
ReadGlobalSetting(Settings::values.anti_aliasing);
|
||||
ReadGlobalSetting(Settings::values.max_anisotropy);
|
||||
ReadGlobalSetting(Settings::values.use_speed_limit);
|
||||
ReadGlobalSetting(Settings::values.speed_limit);
|
||||
ReadGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
ReadGlobalSetting(Settings::values.gpu_accuracy);
|
||||
@@ -784,6 +783,8 @@ void Config::ReadSystemValues() {
|
||||
}
|
||||
}
|
||||
|
||||
ReadBasicSetting(Settings::values.device_name);
|
||||
|
||||
if (global) {
|
||||
ReadBasicSetting(Settings::values.current_user);
|
||||
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
|
||||
@@ -1328,7 +1329,6 @@ void Config::SaveRendererValues() {
|
||||
static_cast<u32>(Settings::values.anti_aliasing.GetDefault()),
|
||||
Settings::values.anti_aliasing.UsingGlobal());
|
||||
WriteGlobalSetting(Settings::values.max_anisotropy);
|
||||
WriteGlobalSetting(Settings::values.use_speed_limit);
|
||||
WriteGlobalSetting(Settings::values.speed_limit);
|
||||
WriteGlobalSetting(Settings::values.use_disk_shader_cache);
|
||||
WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()),
|
||||
@@ -1407,6 +1407,7 @@ void Config::SaveSystemValues() {
|
||||
Settings::values.rng_seed.UsingGlobal());
|
||||
WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0),
|
||||
0, Settings::values.rng_seed.UsingGlobal());
|
||||
WriteBasicSetting(Settings::values.device_name);
|
||||
|
||||
if (global) {
|
||||
WriteBasicSetting(Settings::values.current_user);
|
||||
|
||||
@@ -269,7 +269,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
InputCommon::InputSubsystem* input_subsystem_,
|
||||
InputProfiles* profiles_, Core::HID::HIDCore& hid_core_,
|
||||
bool is_powered_on_, bool debug_)
|
||||
: QWidget(parent),
|
||||
: QScrollArea(parent),
|
||||
ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index{player_index_}, debug{debug_},
|
||||
is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_}, profiles(profiles_),
|
||||
timeout_timer(std::make_unique<QTimer>()),
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QScrollArea>
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings.h"
|
||||
@@ -46,7 +46,7 @@ class EmulatedController;
|
||||
enum class NpadStyleIndex : u8;
|
||||
} // namespace Core::HID
|
||||
|
||||
class ConfigureInputPlayer : public QWidget {
|
||||
class ConfigureInputPlayer : public QScrollArea {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -72,6 +72,8 @@ void ConfigureSystem::SetConfiguration() {
|
||||
ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value());
|
||||
ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value());
|
||||
ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time));
|
||||
ui->device_name_edit->setText(
|
||||
QString::fromUtf8(Settings::values.device_name.GetValue().c_str()));
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
|
||||
@@ -115,6 +117,8 @@ void ConfigureSystem::ApplyConfiguration() {
|
||||
}
|
||||
}
|
||||
|
||||
Settings::values.device_name = ui->device_name_edit->text().toStdString();
|
||||
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -432,6 +432,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="device_name_label">
|
||||
<property name="text">
|
||||
<string>Device Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="combo_sound">
|
||||
<item>
|
||||
@@ -476,6 +483,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLineEdit" name="device_name_edit">
|
||||
<property name="maxLength">
|
||||
<number>128</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="rng_seed_edit">
|
||||
<property name="sizePolicy">
|
||||
|
||||
@@ -554,6 +554,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
|
||||
QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));
|
||||
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"));
|
||||
#ifndef WIN32
|
||||
QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut"));
|
||||
QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop"));
|
||||
QAction* create_applications_menu_shortcut =
|
||||
shortcut_menu->addAction(tr("Add to Applications Menu"));
|
||||
#endif
|
||||
context_menu.addSeparator();
|
||||
QAction* properties = context_menu.addAction(tr("Properties"));
|
||||
|
||||
@@ -619,6 +625,14 @@ 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);
|
||||
});
|
||||
#ifndef WIN32
|
||||
connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() {
|
||||
emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop);
|
||||
});
|
||||
connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() {
|
||||
emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications);
|
||||
});
|
||||
#endif
|
||||
connect(properties, &QAction::triggered,
|
||||
[this, path]() { emit OpenPerGameGeneralRequested(path); });
|
||||
};
|
||||
|
||||
@@ -52,6 +52,11 @@ enum class DumpRomFSTarget {
|
||||
SDMC,
|
||||
};
|
||||
|
||||
enum class GameListShortcutTarget {
|
||||
Desktop,
|
||||
Applications,
|
||||
};
|
||||
|
||||
enum class InstalledEntryType {
|
||||
Game,
|
||||
Update,
|
||||
@@ -108,6 +113,8 @@ signals:
|
||||
const std::string& game_path);
|
||||
void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
|
||||
void CopyTIDRequested(u64 program_id);
|
||||
void CreateShortcut(u64 program_id, const std::string& game_path,
|
||||
GameListShortcutTarget target);
|
||||
void NavigateToGamedbEntryRequested(u64 program_id,
|
||||
const CompatibilityList& compatibility_list);
|
||||
void OpenPerGameGeneralRequested(const std::string& file);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <cinttypes>
|
||||
#include <clocale>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#ifdef __APPLE__
|
||||
@@ -1249,6 +1251,7 @@ void GMainWindow::ConnectWidgetEvents() {
|
||||
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
|
||||
connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
|
||||
&GMainWindow::OnGameListNavigateToGamedbEntry);
|
||||
connect(game_list, &GameList::CreateShortcut, this, &GMainWindow::OnGameListCreateShortcut);
|
||||
connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory);
|
||||
connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this,
|
||||
&GMainWindow::OnGameListAddDirectory);
|
||||
@@ -1787,6 +1790,9 @@ void GMainWindow::ShutdownGame() {
|
||||
|
||||
AllowOSSleep();
|
||||
|
||||
// Disable unlimited frame rate
|
||||
Settings::values.use_speed_limit.SetValue(true);
|
||||
|
||||
system->SetShuttingDown(true);
|
||||
system->DetachDebugger();
|
||||
discord_rpc->Pause();
|
||||
@@ -2376,6 +2382,152 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
|
||||
QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory));
|
||||
}
|
||||
|
||||
void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
|
||||
GameListShortcutTarget target) {
|
||||
// Get path to yuzu executable
|
||||
const QStringList args = QApplication::arguments();
|
||||
std::filesystem::path yuzu_command = args[0].toStdString();
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
// If relative path, make it an absolute path
|
||||
if (yuzu_command.c_str()[0] == '.') {
|
||||
yuzu_command = Common::FS::GetCurrentDir() / yuzu_command;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
// 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 (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__
|
||||
#endif // __linux__ || __FreeBSD__
|
||||
|
||||
std::filesystem::path target_directory{};
|
||||
// Determine target directory for shortcut
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
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");
|
||||
|
||||
if (target == GameListShortcutTarget::Desktop) {
|
||||
target_directory = home_path / "Desktop";
|
||||
if (!Common::FS::IsDir(target_directory)) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Create Shortcut"),
|
||||
tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.")
|
||||
.arg(QString::fromStdString(target_directory)),
|
||||
QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
}
|
||||
} else if (target == GameListShortcutTarget::Applications) {
|
||||
target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) /
|
||||
"applications";
|
||||
if (!Common::FS::CreateDirs(target_directory)) {
|
||||
QMessageBox::critical(this, tr("Create Shortcut"),
|
||||
tr("Cannot create shortcut in applications menu. Path \"%1\" "
|
||||
"does not exist and cannot be created.")
|
||||
.arg(QString::fromStdString(target_directory)),
|
||||
QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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__)
|
||||
std::filesystem::path system_icons_path =
|
||||
(xdg_data_home == nullptr ? home_path / ".local/share/" : 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));
|
||||
#else
|
||||
const std::filesystem::path icon_path{};
|
||||
const std::filesystem::path shortcut_path{};
|
||||
#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_jpeg =
|
||||
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
// Convert and write the icon as a PNG
|
||||
if (!icon_jpeg.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());
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
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;";
|
||||
#else
|
||||
const std::string comment{};
|
||||
const std::string arguments{};
|
||||
const std::string categories{};
|
||||
const std::string keywords{};
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
std::filesystem::path fs_path;
|
||||
if (directory == QStringLiteral("SDMC")) {
|
||||
@@ -2913,9 +3065,15 @@ static QScreen* GuessCurrentScreen(QWidget* window) {
|
||||
});
|
||||
}
|
||||
|
||||
bool GMainWindow::UsingExclusiveFullscreen() {
|
||||
return Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive ||
|
||||
QGuiApplication::platformName() == QStringLiteral("wayland") ||
|
||||
QGuiApplication::platformName() == QStringLiteral("wayland-egl");
|
||||
}
|
||||
|
||||
void GMainWindow::ShowFullscreen() {
|
||||
const auto show_fullscreen = [](QWidget* window) {
|
||||
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
|
||||
const auto show_fullscreen = [this](QWidget* window) {
|
||||
if (UsingExclusiveFullscreen()) {
|
||||
window->showFullScreen();
|
||||
return;
|
||||
}
|
||||
@@ -2943,7 +3101,7 @@ void GMainWindow::ShowFullscreen() {
|
||||
|
||||
void GMainWindow::HideFullscreen() {
|
||||
if (ui->action_Single_Window_Mode->isChecked()) {
|
||||
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
|
||||
if (UsingExclusiveFullscreen()) {
|
||||
showNormal();
|
||||
restoreGeometry(UISettings::values.geometry);
|
||||
} else {
|
||||
@@ -2957,7 +3115,7 @@ void GMainWindow::HideFullscreen() {
|
||||
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
|
||||
ui->menubar->show();
|
||||
} else {
|
||||
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
|
||||
if (UsingExclusiveFullscreen()) {
|
||||
render_window->showNormal();
|
||||
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
|
||||
} else {
|
||||
@@ -3294,6 +3452,38 @@ 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__)
|
||||
// This desktop file template was writting 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;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void GMainWindow::OnLoadAmiibo() {
|
||||
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
|
||||
return;
|
||||
|
||||
@@ -38,6 +38,7 @@ class QProgressDialog;
|
||||
class WaitTreeWidget;
|
||||
enum class GameListOpenTarget;
|
||||
enum class GameListRemoveTarget;
|
||||
enum class GameListShortcutTarget;
|
||||
enum class DumpRomFSTarget;
|
||||
enum class InstalledEntryType;
|
||||
class GameListPlaceholder;
|
||||
@@ -293,6 +294,8 @@ private slots:
|
||||
void OnGameListCopyTID(u64 program_id);
|
||||
void OnGameListNavigateToGamedbEntry(u64 program_id,
|
||||
const CompatibilityList& compatibility_list);
|
||||
void OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
|
||||
GameListShortcutTarget target);
|
||||
void OnGameListOpenDirectory(const QString& directory);
|
||||
void OnGameListAddDirectory();
|
||||
void OnGameListShowList(bool show);
|
||||
@@ -320,6 +323,7 @@ private slots:
|
||||
void OnDisplayTitleBars(bool);
|
||||
void InitializeHotkeys();
|
||||
void ToggleFullscreen();
|
||||
bool UsingExclusiveFullscreen();
|
||||
void ShowFullscreen();
|
||||
void HideFullscreen();
|
||||
void ToggleWindowMode();
|
||||
@@ -365,6 +369,10 @@ private:
|
||||
bool CheckDarkMode();
|
||||
|
||||
QString GetTasStateDescription() const;
|
||||
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);
|
||||
|
||||
std::unique_ptr<Ui::MainWindow> ui;
|
||||
|
||||
|
||||
@@ -138,6 +138,7 @@ struct Values {
|
||||
|
||||
bool configuration_applied;
|
||||
bool reset_to_defaults;
|
||||
bool shortcut_already_warned{false};
|
||||
Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"};
|
||||
};
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ bool EmuWindow_SDL2::IsShown() const {
|
||||
|
||||
void EmuWindow_SDL2::OnResize() {
|
||||
int width, height;
|
||||
SDL_GetWindowSize(render_window, &width, &height);
|
||||
SDL_GL_GetDrawableSize(render_window, &width, &height);
|
||||
UpdateCurrentFramebufferLayout(width, height);
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,8 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
|
||||
exit(1);
|
||||
}
|
||||
|
||||
strict_context_required = strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0;
|
||||
|
||||
SetWindowIcon();
|
||||
|
||||
if (fullscreen) {
|
||||
|
||||
Reference in New Issue
Block a user