Compare commits
32 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60b184377e | ||
|
|
7341257fc4 | ||
|
|
d011f89f15 | ||
|
|
de1ef273b3 | ||
|
|
c2522f3e43 | ||
|
|
d8b83aa8f5 | ||
|
|
65010607b7 | ||
|
|
3c378a31b5 | ||
|
|
74d5c0ed2f | ||
|
|
6f0360690b | ||
|
|
4b9504028d | ||
|
|
55c0dd1cb3 | ||
|
|
7a27b7f3a3 | ||
|
|
b73f678ee8 | ||
|
|
024c84d2db | ||
|
|
0e2ded049d | ||
|
|
670a7f51e8 | ||
|
|
b1a1bd12ca | ||
|
|
bba54e1880 | ||
|
|
1585981eec | ||
|
|
3c8cd62b0d | ||
|
|
33441fa728 | ||
|
|
988e42a3f5 | ||
|
|
16c0373adc | ||
|
|
c4e7ec7a99 | ||
|
|
9de860a419 | ||
|
|
47b97b9577 | ||
|
|
551c61bf27 | ||
|
|
ab9ddab0a2 | ||
|
|
9c065c013e | ||
|
|
8b329ddcc9 | ||
|
|
4e57f9d5cf |
@@ -17,7 +17,7 @@ namespace AudioCore {
|
||||
|
||||
constexpr u32 STREAM_SAMPLE_RATE{48000};
|
||||
constexpr u32 STREAM_NUM_CHANNELS{2};
|
||||
|
||||
using VoiceChannelHolder = std::array<VoiceResourceInformation*, 6>;
|
||||
class AudioRenderer::VoiceState {
|
||||
public:
|
||||
bool IsPlaying() const {
|
||||
@@ -37,9 +37,10 @@ public:
|
||||
}
|
||||
|
||||
void SetWaveIndex(std::size_t index);
|
||||
std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory);
|
||||
std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory,
|
||||
const VoiceChannelHolder& voice_resources);
|
||||
void UpdateState();
|
||||
void RefreshBuffer(Core::Memory::Memory& memory);
|
||||
void RefreshBuffer(Core::Memory::Memory& memory, const VoiceChannelHolder& voice_resources);
|
||||
|
||||
private:
|
||||
bool is_in_use{};
|
||||
@@ -79,7 +80,7 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event,
|
||||
std::size_t instance_number)
|
||||
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
|
||||
effects(params.effect_count), memory{memory_} {
|
||||
voice_resources(params.voice_count), effects(params.effect_count), memory{memory_} {
|
||||
behavior_info.SetUserRevision(params.revision);
|
||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||
stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
|
||||
@@ -127,6 +128,12 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector<
|
||||
input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
|
||||
memory_pool_count * sizeof(MemoryPoolInfo));
|
||||
|
||||
// Copy voice resources
|
||||
const std::size_t voice_resource_offset{sizeof(UpdateDataHeader) + config.behavior_size +
|
||||
config.memory_pools_size};
|
||||
std::memcpy(voice_resources.data(), input_params.data() + voice_resource_offset,
|
||||
sizeof(VoiceResourceInformation) * voice_resources.size());
|
||||
|
||||
// Copy VoiceInfo structs
|
||||
std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size +
|
||||
config.memory_pools_size + config.voice_resource_size};
|
||||
@@ -220,14 +227,15 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
|
||||
is_refresh_pending = true;
|
||||
}
|
||||
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count,
|
||||
Core::Memory::Memory& memory) {
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(
|
||||
std::size_t sample_count, Core::Memory::Memory& memory,
|
||||
const VoiceChannelHolder& voice_resources) {
|
||||
if (!IsPlaying()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (is_refresh_pending) {
|
||||
RefreshBuffer(memory);
|
||||
RefreshBuffer(memory, voice_resources);
|
||||
}
|
||||
|
||||
const std::size_t max_size{samples.size() - offset};
|
||||
@@ -271,7 +279,8 @@ void AudioRenderer::VoiceState::UpdateState() {
|
||||
is_in_use = info.is_in_use;
|
||||
}
|
||||
|
||||
void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) {
|
||||
void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory,
|
||||
const VoiceChannelHolder& voice_resources) {
|
||||
const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr;
|
||||
const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz;
|
||||
std::vector<s16> new_samples(wave_buffer_size / sizeof(s16));
|
||||
@@ -296,17 +305,77 @@ void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) {
|
||||
}
|
||||
|
||||
switch (info.channel_count) {
|
||||
case 1:
|
||||
case 1: {
|
||||
// 1 channel is upsampled to 2 channel
|
||||
samples.resize(new_samples.size() * 2);
|
||||
|
||||
for (std::size_t index = 0; index < new_samples.size(); ++index) {
|
||||
samples[index * 2] = new_samples[index];
|
||||
samples[index * 2 + 1] = new_samples[index];
|
||||
auto sample = static_cast<float>(new_samples[index]);
|
||||
if (voice_resources[0]->in_use) {
|
||||
sample *= voice_resources[0]->mix_volumes[0];
|
||||
}
|
||||
|
||||
samples[index * 2] = static_cast<s16>(sample * info.volume);
|
||||
samples[index * 2 + 1] = static_cast<s16>(sample * info.volume);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// 2 channel is played as is
|
||||
samples = std::move(new_samples);
|
||||
const std::size_t sample_count = (samples.size() / 2);
|
||||
for (std::size_t index = 0; index < sample_count; ++index) {
|
||||
const std::size_t index_l = index * 2;
|
||||
const std::size_t index_r = index * 2 + 1;
|
||||
|
||||
auto sample_l = static_cast<float>(samples[index_l]);
|
||||
auto sample_r = static_cast<float>(samples[index_r]);
|
||||
|
||||
if (voice_resources[0]->in_use) {
|
||||
sample_l *= voice_resources[0]->mix_volumes[0];
|
||||
}
|
||||
|
||||
if (voice_resources[1]->in_use) {
|
||||
sample_r *= voice_resources[1]->mix_volumes[1];
|
||||
}
|
||||
|
||||
samples[index_l] = static_cast<s16>(sample_l * info.volume);
|
||||
samples[index_r] = static_cast<s16>(sample_r * info.volume);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
samples.resize((new_samples.size() / 6) * 2);
|
||||
const std::size_t sample_count = samples.size() / 2;
|
||||
|
||||
for (std::size_t index = 0; index < sample_count; ++index) {
|
||||
auto FL = static_cast<float>(new_samples[index * 6]);
|
||||
auto FR = static_cast<float>(new_samples[index * 6 + 1]);
|
||||
auto FC = static_cast<float>(new_samples[index * 6 + 2]);
|
||||
auto BL = static_cast<float>(new_samples[index * 6 + 4]);
|
||||
auto BR = static_cast<float>(new_samples[index * 6 + 5]);
|
||||
|
||||
if (voice_resources[0]->in_use) {
|
||||
FL *= voice_resources[0]->mix_volumes[0];
|
||||
}
|
||||
if (voice_resources[1]->in_use) {
|
||||
FR *= voice_resources[1]->mix_volumes[1];
|
||||
}
|
||||
if (voice_resources[2]->in_use) {
|
||||
FC *= voice_resources[2]->mix_volumes[2];
|
||||
}
|
||||
if (voice_resources[4]->in_use) {
|
||||
BL *= voice_resources[4]->mix_volumes[4];
|
||||
}
|
||||
if (voice_resources[5]->in_use) {
|
||||
BR *= voice_resources[5]->mix_volumes[5];
|
||||
}
|
||||
|
||||
samples[index * 2] =
|
||||
static_cast<s16>((0.3694f * FL + 0.2612f * FC + 0.3694f * BL) * info.volume);
|
||||
samples[index * 2 + 1] =
|
||||
static_cast<s16>((0.3694f * FR + 0.2612f * FC + 0.3694f * BR) * info.volume);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -352,11 +421,17 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
if (!voice.IsPlaying()) {
|
||||
continue;
|
||||
}
|
||||
VoiceChannelHolder resources{};
|
||||
for (u32 channel = 0; channel < voice.GetInfo().channel_count; channel++) {
|
||||
const auto channel_resource_id = voice.GetInfo().voice_channel_resource_ids[channel];
|
||||
resources[channel] = &voice_resources[channel_resource_id];
|
||||
}
|
||||
|
||||
std::size_t offset{};
|
||||
s64 samples_remaining{BUFFER_SIZE};
|
||||
while (samples_remaining > 0) {
|
||||
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining, memory)};
|
||||
const std::vector<s16> samples{
|
||||
voice.DequeueSamples(samples_remaining, memory, resources)};
|
||||
|
||||
if (samples.empty()) {
|
||||
break;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/behavior_info.h"
|
||||
#include "audio_core/common.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -116,6 +117,14 @@ struct WaveBuffer {
|
||||
};
|
||||
static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
|
||||
|
||||
struct VoiceResourceInformation {
|
||||
s32_le id{};
|
||||
std::array<float_le, MAX_MIX_BUFFERS> mix_volumes{};
|
||||
bool in_use{};
|
||||
INSERT_PADDING_BYTES(11);
|
||||
};
|
||||
static_assert(sizeof(VoiceResourceInformation) == 0x70, "VoiceResourceInformation has wrong size");
|
||||
|
||||
struct VoiceInfo {
|
||||
u32_le id;
|
||||
u32_le node_id;
|
||||
@@ -244,6 +253,7 @@ private:
|
||||
AudioRendererParameter worker_params;
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event;
|
||||
std::vector<VoiceState> voices;
|
||||
std::vector<VoiceResourceInformation> voice_resources;
|
||||
std::vector<EffectState> effects;
|
||||
std::unique_ptr<AudioOut> audio_out;
|
||||
StreamPtr stream;
|
||||
|
||||
@@ -14,6 +14,7 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
|
||||
}
|
||||
|
||||
constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8');
|
||||
constexpr std::size_t MAX_MIX_BUFFERS = 24;
|
||||
|
||||
static constexpr u32 VersionFromRevision(u32_le rev) {
|
||||
// "REV7" -> 7
|
||||
|
||||
@@ -148,6 +148,8 @@ add_library(common STATIC
|
||||
thread.h
|
||||
thread_queue_list.h
|
||||
threadsafe_queue.h
|
||||
time_zone.cpp
|
||||
time_zone.h
|
||||
timer.cpp
|
||||
timer.h
|
||||
uint128.cpp
|
||||
|
||||
49
src/common/time_zone.cpp
Normal file
49
src/common/time_zone.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/time_zone.h"
|
||||
|
||||
namespace Common::TimeZone {
|
||||
|
||||
std::string GetDefaultTimeZone() {
|
||||
return "GMT";
|
||||
}
|
||||
|
||||
static std::string GetOsTimeZoneOffset() {
|
||||
const std::time_t t{std::time(nullptr)};
|
||||
const std::tm tm{*std::localtime(&t)};
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(&tm, "%z"); // Get the current timezone offset, e.g. "-400", as a string
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) {
|
||||
try {
|
||||
return std::stoi(timezone);
|
||||
} catch (const std::invalid_argument&) {
|
||||
LOG_CRITICAL(Common, "invalid_argument with {}!", timezone);
|
||||
return 0;
|
||||
} catch (const std::out_of_range&) {
|
||||
LOG_CRITICAL(Common, "out_of_range with {}!", timezone);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::chrono::seconds GetCurrentOffsetSeconds() {
|
||||
const int offset{ConvertOsTimeZoneOffsetToInt(GetOsTimeZoneOffset())};
|
||||
|
||||
int seconds{(offset / 100) * 60 * 60}; // Convert hour component to seconds
|
||||
seconds += (offset % 100) * 60; // Convert minute component to seconds
|
||||
|
||||
return std::chrono::seconds{seconds};
|
||||
}
|
||||
|
||||
} // namespace Common::TimeZone
|
||||
18
src/common/time_zone.h
Normal file
18
src/common/time_zone.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
namespace Common::TimeZone {
|
||||
|
||||
/// Gets the default timezone, i.e. "GMT"
|
||||
std::string GetDefaultTimeZone();
|
||||
|
||||
/// Gets the offset of the current timezone (from the default), in seconds
|
||||
std::chrono::seconds GetCurrentOffsetSeconds();
|
||||
|
||||
} // namespace Common::TimeZone
|
||||
@@ -95,6 +95,10 @@ u32 NACP::GetSupportedLanguages() const {
|
||||
return raw.supported_languages;
|
||||
}
|
||||
|
||||
u64 NACP::GetDeviceSaveDataSize() const {
|
||||
return raw.device_save_data_size;
|
||||
}
|
||||
|
||||
std::vector<u8> NACP::GetRawBytes() const {
|
||||
std::vector<u8> out(sizeof(RawNACP));
|
||||
std::memcpy(out.data(), &raw, sizeof(RawNACP));
|
||||
|
||||
@@ -113,6 +113,7 @@ public:
|
||||
u32 GetSupportedLanguages() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
bool GetUserAccountSwitchLock() const;
|
||||
u64 GetDeviceSaveDataSize() const;
|
||||
|
||||
private:
|
||||
RawNACP raw{};
|
||||
|
||||
@@ -57,7 +57,8 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
|
||||
bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) {
|
||||
return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage ||
|
||||
(space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User
|
||||
desc.type == SaveDataType::SaveData && desc.title_id == 0 && desc.save_id == 0);
|
||||
(desc.type == SaveDataType::SaveData || desc.type == SaveDataType::DeviceSaveData) &&
|
||||
desc.title_id == 0 && desc.save_id == 0);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
@@ -139,8 +140,10 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
|
||||
u128 user_id, u64 save_id) {
|
||||
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
|
||||
// be interpreted as the title id of the current process.
|
||||
if (type == SaveDataType::SaveData && title_id == 0) {
|
||||
title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
|
||||
if (title_id == 0) {
|
||||
title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
}
|
||||
}
|
||||
|
||||
std::string out = GetSaveDataSpaceIdPath(space);
|
||||
|
||||
@@ -46,7 +46,7 @@ private:
|
||||
EmuWindow::EmuWindow() {
|
||||
// TODO: Find a better place to set this.
|
||||
config.min_client_area_size =
|
||||
std::make_pair(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||
std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
|
||||
active_config = config;
|
||||
touch_state = std::make_shared<TouchState>();
|
||||
Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
namespace Layout {
|
||||
|
||||
namespace MinimumSize {
|
||||
constexpr u32 Width = 640;
|
||||
constexpr u32 Height = 360;
|
||||
} // namespace MinimumSize
|
||||
|
||||
namespace ScreenUndocked {
|
||||
constexpr u32 Width = 1280;
|
||||
constexpr u32 Height = 720;
|
||||
|
||||
@@ -767,7 +767,7 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
|
||||
{1014, nullptr, "OutputMultiProgramTagAccessLog"},
|
||||
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
|
||||
{1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
|
||||
{1200, nullptr, "OpenMultiCommitManager"},
|
||||
{1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"},
|
||||
{1300, nullptr, "OpenBisWiper"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -988,4 +988,40 @@ void FSP_SRV::GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(access_log_program_index);
|
||||
}
|
||||
|
||||
class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> {
|
||||
public:
|
||||
explicit IMultiCommitManager() : ServiceFramework("IMultiCommitManager") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IMultiCommitManager::Add, "Add"},
|
||||
{2, &IMultiCommitManager::Commit, "Commit"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
FileSys::VirtualFile backend;
|
||||
|
||||
void Add(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Commit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
};
|
||||
|
||||
void FSP_SRV::OpenMultiCommitManager(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IMultiCommitManager>(std::make_shared<IMultiCommitManager>());
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
||||
@@ -50,6 +50,7 @@ private:
|
||||
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
|
||||
void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
|
||||
void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
|
||||
|
||||
FileSystemController& fsc;
|
||||
|
||||
|
||||
@@ -138,9 +138,7 @@ u32 BufferQueue::Query(QueryType type) {
|
||||
|
||||
switch (type) {
|
||||
case QueryType::NativeWindowFormat:
|
||||
// TODO(Subv): Use an enum for this
|
||||
static constexpr u32 FormatABGR8 = 1;
|
||||
return FormatABGR8;
|
||||
return static_cast<u32>(PixelFormat::RGBA8888);
|
||||
}
|
||||
|
||||
UNIMPLEMENTED();
|
||||
|
||||
@@ -66,6 +66,16 @@ public:
|
||||
Rotate270 = 0x07,
|
||||
};
|
||||
|
||||
enum class PixelFormat : u32 {
|
||||
RGBA8888 = 1,
|
||||
RGBX8888 = 2,
|
||||
RGB888 = 3,
|
||||
RGB565 = 4,
|
||||
BGRA8888 = 5,
|
||||
RGBA5551 = 6,
|
||||
RRGBA4444 = 7,
|
||||
};
|
||||
|
||||
struct Buffer {
|
||||
enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
#include "common/time_zone.h"
|
||||
#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h"
|
||||
#include "core/hle/service/time/local_system_clock_context_writer.h"
|
||||
#include "core/hle/service/time/network_system_clock_context_writer.h"
|
||||
@@ -21,8 +22,16 @@ static std::chrono::seconds GetSecondsSinceEpoch() {
|
||||
Settings::values.custom_rtc_differential;
|
||||
}
|
||||
|
||||
static s64 GetExternalTimeZoneOffset() {
|
||||
// With "auto" timezone setting, we use the external system's timezone offset
|
||||
if (Settings::GetTimeZoneString() == "auto") {
|
||||
return Common::TimeZone::GetCurrentOffsetSeconds().count();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s64 GetExternalRtcValue() {
|
||||
return GetSecondsSinceEpoch().count();
|
||||
return GetSecondsSinceEpoch().count() + GetExternalTimeZoneOffset();
|
||||
}
|
||||
|
||||
TimeManager::TimeManager(Core::System& system)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/time_zone.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
@@ -14,6 +15,7 @@
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
#include "core/hle/service/time/time_zone_content_manager.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::Time::TimeZone {
|
||||
|
||||
@@ -68,10 +70,22 @@ static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
|
||||
|
||||
TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system)
|
||||
: system{system}, location_name_cache{BuildLocationNameCache(system)} {
|
||||
if (FileSys::VirtualFile vfs_file; GetTimeZoneInfoFile("GMT", vfs_file) == RESULT_SUCCESS) {
|
||||
|
||||
std::string location_name;
|
||||
const auto timezone_setting = Settings::GetTimeZoneString();
|
||||
if (timezone_setting == "auto") {
|
||||
location_name = Common::TimeZone::GetDefaultTimeZone();
|
||||
} else if (timezone_setting == "default") {
|
||||
location_name = location_name;
|
||||
} else {
|
||||
location_name = timezone_setting;
|
||||
}
|
||||
|
||||
if (FileSys::VirtualFile vfs_file;
|
||||
GetTimeZoneInfoFile(location_name, vfs_file) == RESULT_SUCCESS) {
|
||||
const auto time_point{
|
||||
time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
|
||||
time_manager.SetupTimeZoneManager("GMT", time_point, location_name_cache.size(), {},
|
||||
time_manager.SetupTimeZoneManager(location_name, time_point, location_name_cache.size(), {},
|
||||
vfs_file);
|
||||
} else {
|
||||
time_zone_manager.MarkAsInitialized();
|
||||
@@ -113,6 +127,12 @@ ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& locati
|
||||
}
|
||||
|
||||
vfs_file = zoneinfo_dir->GetFile(location_name);
|
||||
if (!vfs_file) {
|
||||
LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.",
|
||||
time_zone_binary_titleid, location_name);
|
||||
vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone());
|
||||
}
|
||||
|
||||
if (!vfs_file) {
|
||||
LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid,
|
||||
location_name);
|
||||
|
||||
@@ -63,6 +63,21 @@ const std::array<const char*, NumMouseButtons> mapping = {{
|
||||
|
||||
Values values = {};
|
||||
|
||||
std::string GetTimeZoneString() {
|
||||
static constexpr std::array<const char*, 46> timezones{{
|
||||
"auto", "default", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire",
|
||||
"EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0",
|
||||
"Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan",
|
||||
"Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT",
|
||||
"Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey",
|
||||
"UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
|
||||
}};
|
||||
|
||||
ASSERT(Settings::values.time_zone_index < timezones.size());
|
||||
|
||||
return timezones[Settings::values.time_zone_index];
|
||||
}
|
||||
|
||||
void Apply() {
|
||||
GDBStub::SetServerPort(values.gdbstub_port);
|
||||
GDBStub::ToggleServer(values.use_gdbstub);
|
||||
@@ -87,6 +102,7 @@ void LogSettings() {
|
||||
LogSetting("System_CurrentUser", Settings::values.current_user);
|
||||
LogSetting("System_LanguageIndex", Settings::values.language_index);
|
||||
LogSetting("System_RegionIndex", Settings::values.region_index);
|
||||
LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index);
|
||||
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
|
||||
@@ -394,6 +394,7 @@ struct Values {
|
||||
s32 current_user;
|
||||
s32 language_index;
|
||||
s32 region_index;
|
||||
s32 time_zone_index;
|
||||
s32 sound_index;
|
||||
|
||||
// Controls
|
||||
@@ -490,6 +491,9 @@ struct Values {
|
||||
bool IsGPULevelExtreme();
|
||||
bool IsGPULevelHigh();
|
||||
|
||||
std::string GetTimeZoneString();
|
||||
|
||||
void Apply();
|
||||
void LogSettings();
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -54,9 +54,7 @@ bool DmaPusher::Step() {
|
||||
return true;
|
||||
});
|
||||
const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]};
|
||||
GPUVAddr dma_get = command_list_header.addr;
|
||||
GPUVAddr dma_put = dma_get + command_list_header.size * sizeof(u32);
|
||||
bool non_main = command_list_header.is_non_main;
|
||||
const GPUVAddr dma_get = command_list_header.addr;
|
||||
|
||||
if (dma_pushbuffer_subindex >= command_list.size()) {
|
||||
// We've gone through the current list, remove it from the queue
|
||||
@@ -133,11 +131,6 @@ bool DmaPusher::Step() {
|
||||
index++;
|
||||
}
|
||||
|
||||
if (!non_main) {
|
||||
// TODO (degasus): This is dead code, as dma_mget is never read.
|
||||
dma_mget = dma_put;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,6 @@ private:
|
||||
DmaState dma_state{};
|
||||
bool dma_increment_once{};
|
||||
|
||||
GPUVAddr dma_mget{}; ///< main pushbuffer last read address
|
||||
bool ib_enable{true}; ///< IB mode enabled
|
||||
|
||||
std::array<Tegra::Engines::EngineInterface*, max_subchannels> subchannels{};
|
||||
|
||||
@@ -168,18 +168,22 @@ enum class Pred : u64 {
|
||||
};
|
||||
|
||||
enum class PredCondition : u64 {
|
||||
LessThan = 1,
|
||||
Equal = 2,
|
||||
LessEqual = 3,
|
||||
GreaterThan = 4,
|
||||
NotEqual = 5,
|
||||
GreaterEqual = 6,
|
||||
LessThanWithNan = 9,
|
||||
LessEqualWithNan = 11,
|
||||
GreaterThanWithNan = 12,
|
||||
NotEqualWithNan = 13,
|
||||
GreaterEqualWithNan = 14,
|
||||
// TODO(Subv): Other condition types
|
||||
F = 0, // Always false
|
||||
LT = 1, // Ordered less than
|
||||
EQ = 2, // Ordered equal
|
||||
LE = 3, // Ordered less than or equal
|
||||
GT = 4, // Ordered greater than
|
||||
NE = 5, // Ordered not equal
|
||||
GE = 6, // Ordered greater than or equal
|
||||
NUM = 7, // Ordered
|
||||
NAN_ = 8, // Unordered
|
||||
LTU = 9, // Unordered less than
|
||||
EQU = 10, // Unordered equal
|
||||
LEU = 11, // Unordered less than or equal
|
||||
GTU = 12, // Unordered greater than
|
||||
NEU = 13, // Unordered not equal
|
||||
GEU = 14, // Unordered greater than or equal
|
||||
T = 15, // Always true
|
||||
};
|
||||
|
||||
enum class PredOperation : u64 {
|
||||
|
||||
@@ -1840,34 +1840,40 @@ private:
|
||||
Type::HalfFloat};
|
||||
}
|
||||
|
||||
template <Type type>
|
||||
Expression LogicalLessThan(Operation operation) {
|
||||
return GenerateBinaryInfix(operation, "<", Type::Bool, type, type);
|
||||
template <const std::string_view& op, Type type, bool unordered = false>
|
||||
Expression Comparison(Operation operation) {
|
||||
static_assert(!unordered || type == Type::Float);
|
||||
|
||||
const Expression expr = GenerateBinaryInfix(operation, op, Type::Bool, type, type);
|
||||
|
||||
if constexpr (op.compare("!=") == 0 && type == Type::Float && !unordered) {
|
||||
// GLSL's operator!=(float, float) doesn't seem be ordered. This happens on both AMD's
|
||||
// and Nvidia's proprietary stacks. Manually force an ordered comparison.
|
||||
return {fmt::format("({} && !isnan({}) && !isnan({}))", expr.AsBool(),
|
||||
VisitOperand(operation, 0).AsFloat(),
|
||||
VisitOperand(operation, 1).AsFloat()),
|
||||
Type::Bool};
|
||||
}
|
||||
if constexpr (!unordered) {
|
||||
return expr;
|
||||
}
|
||||
// Unordered comparisons are always true for NaN operands.
|
||||
return {fmt::format("({} || isnan({}) || isnan({}))", expr.AsBool(),
|
||||
VisitOperand(operation, 0).AsFloat(),
|
||||
VisitOperand(operation, 1).AsFloat()),
|
||||
Type::Bool};
|
||||
}
|
||||
|
||||
template <Type type>
|
||||
Expression LogicalEqual(Operation operation) {
|
||||
return GenerateBinaryInfix(operation, "==", Type::Bool, type, type);
|
||||
Expression FOrdered(Operation operation) {
|
||||
return {fmt::format("(!isnan({}) && !isnan({}))", VisitOperand(operation, 0).AsFloat(),
|
||||
VisitOperand(operation, 1).AsFloat()),
|
||||
Type::Bool};
|
||||
}
|
||||
|
||||
template <Type type>
|
||||
Expression LogicalLessEqual(Operation operation) {
|
||||
return GenerateBinaryInfix(operation, "<=", Type::Bool, type, type);
|
||||
}
|
||||
|
||||
template <Type type>
|
||||
Expression LogicalGreaterThan(Operation operation) {
|
||||
return GenerateBinaryInfix(operation, ">", Type::Bool, type, type);
|
||||
}
|
||||
|
||||
template <Type type>
|
||||
Expression LogicalNotEqual(Operation operation) {
|
||||
return GenerateBinaryInfix(operation, "!=", Type::Bool, type, type);
|
||||
}
|
||||
|
||||
template <Type type>
|
||||
Expression LogicalGreaterEqual(Operation operation) {
|
||||
return GenerateBinaryInfix(operation, ">=", Type::Bool, type, type);
|
||||
Expression FUnordered(Operation operation) {
|
||||
return {fmt::format("(isnan({}) || isnan({}))", VisitOperand(operation, 0).AsFloat(),
|
||||
VisitOperand(operation, 1).AsFloat()),
|
||||
Type::Bool};
|
||||
}
|
||||
|
||||
Expression LogicalAddCarry(Operation operation) {
|
||||
@@ -2324,6 +2330,13 @@ private:
|
||||
Func() = delete;
|
||||
~Func() = delete;
|
||||
|
||||
static constexpr std::string_view LessThan = "<";
|
||||
static constexpr std::string_view Equal = "==";
|
||||
static constexpr std::string_view LessEqual = "<=";
|
||||
static constexpr std::string_view GreaterThan = ">";
|
||||
static constexpr std::string_view NotEqual = "!=";
|
||||
static constexpr std::string_view GreaterEqual = ">=";
|
||||
|
||||
static constexpr std::string_view Add = "Add";
|
||||
static constexpr std::string_view Min = "Min";
|
||||
static constexpr std::string_view Max = "Max";
|
||||
@@ -2425,27 +2438,34 @@ private:
|
||||
&GLSLDecompiler::LogicalPick2,
|
||||
&GLSLDecompiler::LogicalAnd2,
|
||||
|
||||
&GLSLDecompiler::LogicalLessThan<Type::Float>,
|
||||
&GLSLDecompiler::LogicalEqual<Type::Float>,
|
||||
&GLSLDecompiler::LogicalLessEqual<Type::Float>,
|
||||
&GLSLDecompiler::LogicalGreaterThan<Type::Float>,
|
||||
&GLSLDecompiler::LogicalNotEqual<Type::Float>,
|
||||
&GLSLDecompiler::LogicalGreaterEqual<Type::Float>,
|
||||
&GLSLDecompiler::LogicalFIsNan,
|
||||
&GLSLDecompiler::Comparison<Func::LessThan, Type::Float, false>,
|
||||
&GLSLDecompiler::Comparison<Func::Equal, Type::Float, false>,
|
||||
&GLSLDecompiler::Comparison<Func::LessEqual, Type::Float, false>,
|
||||
&GLSLDecompiler::Comparison<Func::GreaterThan, Type::Float, false>,
|
||||
&GLSLDecompiler::Comparison<Func::NotEqual, Type::Float, false>,
|
||||
&GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Float, false>,
|
||||
&GLSLDecompiler::FOrdered,
|
||||
&GLSLDecompiler::FUnordered,
|
||||
&GLSLDecompiler::Comparison<Func::LessThan, Type::Float, true>,
|
||||
&GLSLDecompiler::Comparison<Func::Equal, Type::Float, true>,
|
||||
&GLSLDecompiler::Comparison<Func::LessEqual, Type::Float, true>,
|
||||
&GLSLDecompiler::Comparison<Func::GreaterThan, Type::Float, true>,
|
||||
&GLSLDecompiler::Comparison<Func::NotEqual, Type::Float, true>,
|
||||
&GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Float, true>,
|
||||
|
||||
&GLSLDecompiler::LogicalLessThan<Type::Int>,
|
||||
&GLSLDecompiler::LogicalEqual<Type::Int>,
|
||||
&GLSLDecompiler::LogicalLessEqual<Type::Int>,
|
||||
&GLSLDecompiler::LogicalGreaterThan<Type::Int>,
|
||||
&GLSLDecompiler::LogicalNotEqual<Type::Int>,
|
||||
&GLSLDecompiler::LogicalGreaterEqual<Type::Int>,
|
||||
&GLSLDecompiler::Comparison<Func::LessThan, Type::Int>,
|
||||
&GLSLDecompiler::Comparison<Func::Equal, Type::Int>,
|
||||
&GLSLDecompiler::Comparison<Func::LessEqual, Type::Int>,
|
||||
&GLSLDecompiler::Comparison<Func::GreaterThan, Type::Int>,
|
||||
&GLSLDecompiler::Comparison<Func::NotEqual, Type::Int>,
|
||||
&GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Int>,
|
||||
|
||||
&GLSLDecompiler::LogicalLessThan<Type::Uint>,
|
||||
&GLSLDecompiler::LogicalEqual<Type::Uint>,
|
||||
&GLSLDecompiler::LogicalLessEqual<Type::Uint>,
|
||||
&GLSLDecompiler::LogicalGreaterThan<Type::Uint>,
|
||||
&GLSLDecompiler::LogicalNotEqual<Type::Uint>,
|
||||
&GLSLDecompiler::LogicalGreaterEqual<Type::Uint>,
|
||||
&GLSLDecompiler::Comparison<Func::LessThan, Type::Uint>,
|
||||
&GLSLDecompiler::Comparison<Func::Equal, Type::Uint>,
|
||||
&GLSLDecompiler::Comparison<Func::LessEqual, Type::Uint>,
|
||||
&GLSLDecompiler::Comparison<Func::GreaterThan, Type::Uint>,
|
||||
&GLSLDecompiler::Comparison<Func::NotEqual, Type::Uint>,
|
||||
&GLSLDecompiler::Comparison<Func::GreaterEqual, Type::Uint>,
|
||||
|
||||
&GLSLDecompiler::LogicalAddCarry,
|
||||
|
||||
|
||||
@@ -569,7 +569,9 @@ void RasterizerVulkan::ReleaseFences() {
|
||||
}
|
||||
|
||||
void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
||||
FlushRegion(addr, size);
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
FlushRegion(addr, size);
|
||||
}
|
||||
InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
|
||||
@@ -1618,6 +1618,24 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
Expression LogicalFOrdered(Operation operation) {
|
||||
// Emulate SPIR-V's OpOrdered
|
||||
const Id op_a = AsFloat(Visit(operation[0]));
|
||||
const Id op_b = AsFloat(Visit(operation[1]));
|
||||
const Id is_num_a = OpFOrdEqual(t_bool, op_a, op_a);
|
||||
const Id is_num_b = OpFOrdEqual(t_bool, op_b, op_b);
|
||||
return {OpLogicalAnd(t_bool, is_num_a, is_num_b), Type::Bool};
|
||||
}
|
||||
|
||||
Expression LogicalFUnordered(Operation operation) {
|
||||
// Emulate SPIR-V's OpUnordered
|
||||
const Id op_a = AsFloat(Visit(operation[0]));
|
||||
const Id op_b = AsFloat(Visit(operation[1]));
|
||||
const Id is_nan_a = OpIsNan(t_bool, op_a);
|
||||
const Id is_nan_b = OpIsNan(t_bool, op_b);
|
||||
return {OpLogicalOr(t_bool, is_nan_a, is_nan_b), Type::Bool};
|
||||
}
|
||||
|
||||
Id GetTextureSampler(Operation operation) {
|
||||
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
||||
ASSERT(!meta.sampler.is_buffer);
|
||||
@@ -2511,7 +2529,14 @@ private:
|
||||
&SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::Float>,
|
||||
&SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::Float>,
|
||||
&SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::Float>,
|
||||
&SPIRVDecompiler::Unary<&Module::OpIsNan, Type::Bool, Type::Float>,
|
||||
&SPIRVDecompiler::LogicalFOrdered,
|
||||
&SPIRVDecompiler::LogicalFUnordered,
|
||||
&SPIRVDecompiler::Binary<&Module::OpFUnordLessThan, Type::Bool, Type::Float>,
|
||||
&SPIRVDecompiler::Binary<&Module::OpFUnordEqual, Type::Bool, Type::Float>,
|
||||
&SPIRVDecompiler::Binary<&Module::OpFUnordLessThanEqual, Type::Bool, Type::Float>,
|
||||
&SPIRVDecompiler::Binary<&Module::OpFUnordGreaterThan, Type::Bool, Type::Float>,
|
||||
&SPIRVDecompiler::Binary<&Module::OpFUnordNotEqual, Type::Bool, Type::Float>,
|
||||
&SPIRVDecompiler::Binary<&Module::OpFUnordGreaterThanEqual, Type::Bool, Type::Float>,
|
||||
|
||||
&SPIRVDecompiler::Binary<&Module::OpSLessThan, Type::Bool, Type::Int>,
|
||||
&SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Int>,
|
||||
|
||||
@@ -255,7 +255,7 @@ void ShaderIR::InsertControlFlow(NodeBlock& bb, const ShaderBlock& block) {
|
||||
Node n = Operation(OperationCode::Branch, Immediate(branch_case.address));
|
||||
Node op_b = Immediate(branch_case.cmp_value);
|
||||
Node condition =
|
||||
GetPredicateComparisonInteger(Tegra::Shader::PredCondition::Equal, false, op_a, op_b);
|
||||
GetPredicateComparisonInteger(Tegra::Shader::PredCondition::EQ, false, op_a, op_b);
|
||||
auto result = Conditional(condition, {n});
|
||||
bb.push_back(result);
|
||||
global_code.push_back(result);
|
||||
|
||||
@@ -97,19 +97,19 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
|
||||
return SignedOperation(OperationCode::IAdd, is_signed_c, original_c, shifted_b);
|
||||
}
|
||||
case Tegra::Shader::XmadMode::CSfu: {
|
||||
const Node comp_a = GetPredicateComparisonInteger(PredCondition::Equal, is_signed_a,
|
||||
op_a, Immediate(0));
|
||||
const Node comp_b = GetPredicateComparisonInteger(PredCondition::Equal, is_signed_b,
|
||||
op_b, Immediate(0));
|
||||
const Node comp_a =
|
||||
GetPredicateComparisonInteger(PredCondition::EQ, is_signed_a, op_a, Immediate(0));
|
||||
const Node comp_b =
|
||||
GetPredicateComparisonInteger(PredCondition::EQ, is_signed_b, op_b, Immediate(0));
|
||||
const Node comp = Operation(OperationCode::LogicalOr, comp_a, comp_b);
|
||||
|
||||
const Node comp_minus_a = GetPredicateComparisonInteger(
|
||||
PredCondition::NotEqual, is_signed_a,
|
||||
PredCondition::NE, is_signed_a,
|
||||
SignedOperation(OperationCode::IBitwiseAnd, is_signed_a, op_a,
|
||||
Immediate(0x80000000)),
|
||||
Immediate(0));
|
||||
const Node comp_minus_b = GetPredicateComparisonInteger(
|
||||
PredCondition::NotEqual, is_signed_b,
|
||||
PredCondition::NE, is_signed_b,
|
||||
SignedOperation(OperationCode::IBitwiseAnd, is_signed_b, op_b,
|
||||
Immediate(0x80000000)),
|
||||
Immediate(0));
|
||||
|
||||
@@ -110,13 +110,20 @@ enum class OperationCode {
|
||||
LogicalPick2, /// (bool2 pair, uint index) -> bool
|
||||
LogicalAnd2, /// (bool2 a) -> bool
|
||||
|
||||
LogicalFLessThan, /// (float a, float b) -> bool
|
||||
LogicalFEqual, /// (float a, float b) -> bool
|
||||
LogicalFLessEqual, /// (float a, float b) -> bool
|
||||
LogicalFGreaterThan, /// (float a, float b) -> bool
|
||||
LogicalFNotEqual, /// (float a, float b) -> bool
|
||||
LogicalFGreaterEqual, /// (float a, float b) -> bool
|
||||
LogicalFIsNan, /// (float a) -> bool
|
||||
LogicalFOrdLessThan, /// (float a, float b) -> bool
|
||||
LogicalFOrdEqual, /// (float a, float b) -> bool
|
||||
LogicalFOrdLessEqual, /// (float a, float b) -> bool
|
||||
LogicalFOrdGreaterThan, /// (float a, float b) -> bool
|
||||
LogicalFOrdNotEqual, /// (float a, float b) -> bool
|
||||
LogicalFOrdGreaterEqual, /// (float a, float b) -> bool
|
||||
LogicalFOrdered, /// (float a, float b) -> bool
|
||||
LogicalFUnordered, /// (float a, float b) -> bool
|
||||
LogicalFUnordLessThan, /// (float a, float b) -> bool
|
||||
LogicalFUnordEqual, /// (float a, float b) -> bool
|
||||
LogicalFUnordLessEqual, /// (float a, float b) -> bool
|
||||
LogicalFUnordGreaterThan, /// (float a, float b) -> bool
|
||||
LogicalFUnordNotEqual, /// (float a, float b) -> bool
|
||||
LogicalFUnordGreaterEqual, /// (float a, float b) -> bool
|
||||
|
||||
LogicalILessThan, /// (int a, int b) -> bool
|
||||
LogicalIEqual, /// (int a, int b) -> bool
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/registry.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
@@ -243,56 +244,44 @@ Node ShaderIR::GetSaturatedHalfFloat(Node value, bool saturate) {
|
||||
}
|
||||
|
||||
Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, Node op_b) {
|
||||
static constexpr std::array comparison_table{
|
||||
std::pair{PredCondition::LessThan, OperationCode::LogicalFLessThan},
|
||||
std::pair{PredCondition::Equal, OperationCode::LogicalFEqual},
|
||||
std::pair{PredCondition::LessEqual, OperationCode::LogicalFLessEqual},
|
||||
std::pair{PredCondition::GreaterThan, OperationCode::LogicalFGreaterThan},
|
||||
std::pair{PredCondition::NotEqual, OperationCode::LogicalFNotEqual},
|
||||
std::pair{PredCondition::GreaterEqual, OperationCode::LogicalFGreaterEqual},
|
||||
std::pair{PredCondition::LessThanWithNan, OperationCode::LogicalFLessThan},
|
||||
std::pair{PredCondition::NotEqualWithNan, OperationCode::LogicalFNotEqual},
|
||||
std::pair{PredCondition::LessEqualWithNan, OperationCode::LogicalFLessEqual},
|
||||
std::pair{PredCondition::GreaterThanWithNan, OperationCode::LogicalFGreaterThan},
|
||||
std::pair{PredCondition::GreaterEqualWithNan, OperationCode::LogicalFGreaterEqual},
|
||||
};
|
||||
|
||||
const auto comparison =
|
||||
std::find_if(comparison_table.cbegin(), comparison_table.cend(),
|
||||
[condition](const auto entry) { return condition == entry.first; });
|
||||
UNIMPLEMENTED_IF_MSG(comparison == comparison_table.cend(),
|
||||
"Unknown predicate comparison operation");
|
||||
|
||||
Node predicate = Operation(comparison->second, NO_PRECISE, op_a, op_b);
|
||||
|
||||
if (condition == PredCondition::LessThanWithNan ||
|
||||
condition == PredCondition::NotEqualWithNan ||
|
||||
condition == PredCondition::LessEqualWithNan ||
|
||||
condition == PredCondition::GreaterThanWithNan ||
|
||||
condition == PredCondition::GreaterEqualWithNan) {
|
||||
predicate = Operation(OperationCode::LogicalOr, predicate,
|
||||
Operation(OperationCode::LogicalFIsNan, op_a));
|
||||
predicate = Operation(OperationCode::LogicalOr, predicate,
|
||||
Operation(OperationCode::LogicalFIsNan, op_b));
|
||||
if (condition == PredCondition::T) {
|
||||
return GetPredicate(true);
|
||||
} else if (condition == PredCondition::F) {
|
||||
return GetPredicate(false);
|
||||
}
|
||||
|
||||
return predicate;
|
||||
static constexpr std::array comparison_table{
|
||||
OperationCode(0),
|
||||
OperationCode::LogicalFOrdLessThan, // LT
|
||||
OperationCode::LogicalFOrdEqual, // EQ
|
||||
OperationCode::LogicalFOrdLessEqual, // LE
|
||||
OperationCode::LogicalFOrdGreaterThan, // GT
|
||||
OperationCode::LogicalFOrdNotEqual, // NE
|
||||
OperationCode::LogicalFOrdGreaterEqual, // GE
|
||||
OperationCode::LogicalFOrdered, // NUM
|
||||
OperationCode::LogicalFUnordered, // NAN
|
||||
OperationCode::LogicalFUnordLessThan, // LTU
|
||||
OperationCode::LogicalFUnordEqual, // EQU
|
||||
OperationCode::LogicalFUnordLessEqual, // LEU
|
||||
OperationCode::LogicalFUnordGreaterThan, // GTU
|
||||
OperationCode::LogicalFUnordNotEqual, // NEU
|
||||
OperationCode::LogicalFUnordGreaterEqual, // GEU
|
||||
};
|
||||
const std::size_t index = static_cast<std::size_t>(condition);
|
||||
ASSERT_MSG(index < std::size(comparison_table), "Invalid condition={}", index);
|
||||
|
||||
return Operation(comparison_table[index], op_a, op_b);
|
||||
}
|
||||
|
||||
Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_signed, Node op_a,
|
||||
Node op_b) {
|
||||
static constexpr std::array comparison_table{
|
||||
std::pair{PredCondition::LessThan, OperationCode::LogicalILessThan},
|
||||
std::pair{PredCondition::Equal, OperationCode::LogicalIEqual},
|
||||
std::pair{PredCondition::LessEqual, OperationCode::LogicalILessEqual},
|
||||
std::pair{PredCondition::GreaterThan, OperationCode::LogicalIGreaterThan},
|
||||
std::pair{PredCondition::NotEqual, OperationCode::LogicalINotEqual},
|
||||
std::pair{PredCondition::GreaterEqual, OperationCode::LogicalIGreaterEqual},
|
||||
std::pair{PredCondition::LessThanWithNan, OperationCode::LogicalILessThan},
|
||||
std::pair{PredCondition::NotEqualWithNan, OperationCode::LogicalINotEqual},
|
||||
std::pair{PredCondition::LessEqualWithNan, OperationCode::LogicalILessEqual},
|
||||
std::pair{PredCondition::GreaterThanWithNan, OperationCode::LogicalIGreaterThan},
|
||||
std::pair{PredCondition::GreaterEqualWithNan, OperationCode::LogicalIGreaterEqual},
|
||||
std::pair{PredCondition::LT, OperationCode::LogicalILessThan},
|
||||
std::pair{PredCondition::EQ, OperationCode::LogicalIEqual},
|
||||
std::pair{PredCondition::LE, OperationCode::LogicalILessEqual},
|
||||
std::pair{PredCondition::GT, OperationCode::LogicalIGreaterThan},
|
||||
std::pair{PredCondition::NE, OperationCode::LogicalINotEqual},
|
||||
std::pair{PredCondition::GE, OperationCode::LogicalIGreaterEqual},
|
||||
};
|
||||
|
||||
const auto comparison =
|
||||
@@ -301,32 +290,24 @@ Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_si
|
||||
UNIMPLEMENTED_IF_MSG(comparison == comparison_table.cend(),
|
||||
"Unknown predicate comparison operation");
|
||||
|
||||
Node predicate = SignedOperation(comparison->second, is_signed, NO_PRECISE, std::move(op_a),
|
||||
std::move(op_b));
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(condition == PredCondition::LessThanWithNan ||
|
||||
condition == PredCondition::NotEqualWithNan ||
|
||||
condition == PredCondition::LessEqualWithNan ||
|
||||
condition == PredCondition::GreaterThanWithNan ||
|
||||
condition == PredCondition::GreaterEqualWithNan,
|
||||
"NaN comparisons for integers are not implemented");
|
||||
return predicate;
|
||||
return SignedOperation(comparison->second, is_signed, NO_PRECISE, std::move(op_a),
|
||||
std::move(op_b));
|
||||
}
|
||||
|
||||
Node ShaderIR::GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node op_a,
|
||||
Node op_b) {
|
||||
static constexpr std::array comparison_table{
|
||||
std::pair{PredCondition::LessThan, OperationCode::Logical2HLessThan},
|
||||
std::pair{PredCondition::Equal, OperationCode::Logical2HEqual},
|
||||
std::pair{PredCondition::LessEqual, OperationCode::Logical2HLessEqual},
|
||||
std::pair{PredCondition::GreaterThan, OperationCode::Logical2HGreaterThan},
|
||||
std::pair{PredCondition::NotEqual, OperationCode::Logical2HNotEqual},
|
||||
std::pair{PredCondition::GreaterEqual, OperationCode::Logical2HGreaterEqual},
|
||||
std::pair{PredCondition::LessThanWithNan, OperationCode::Logical2HLessThanWithNan},
|
||||
std::pair{PredCondition::NotEqualWithNan, OperationCode::Logical2HNotEqualWithNan},
|
||||
std::pair{PredCondition::LessEqualWithNan, OperationCode::Logical2HLessEqualWithNan},
|
||||
std::pair{PredCondition::GreaterThanWithNan, OperationCode::Logical2HGreaterThanWithNan},
|
||||
std::pair{PredCondition::GreaterEqualWithNan, OperationCode::Logical2HGreaterEqualWithNan},
|
||||
std::pair{PredCondition::LT, OperationCode::Logical2HLessThan},
|
||||
std::pair{PredCondition::EQ, OperationCode::Logical2HEqual},
|
||||
std::pair{PredCondition::LE, OperationCode::Logical2HLessEqual},
|
||||
std::pair{PredCondition::GT, OperationCode::Logical2HGreaterThan},
|
||||
std::pair{PredCondition::NE, OperationCode::Logical2HNotEqual},
|
||||
std::pair{PredCondition::GE, OperationCode::Logical2HGreaterEqual},
|
||||
std::pair{PredCondition::LTU, OperationCode::Logical2HLessThanWithNan},
|
||||
std::pair{PredCondition::LEU, OperationCode::Logical2HLessEqualWithNan},
|
||||
std::pair{PredCondition::GTU, OperationCode::Logical2HGreaterThanWithNan},
|
||||
std::pair{PredCondition::NEU, OperationCode::Logical2HNotEqualWithNan},
|
||||
std::pair{PredCondition::GEU, OperationCode::Logical2HGreaterEqualWithNan},
|
||||
};
|
||||
|
||||
const auto comparison =
|
||||
@@ -397,7 +378,7 @@ void ShaderIR::SetInternalFlagsFromFloat(NodeBlock& bb, Node value, bool sets_cc
|
||||
if (!sets_cc) {
|
||||
return;
|
||||
}
|
||||
Node zerop = Operation(OperationCode::LogicalFEqual, std::move(value), Immediate(0.0f));
|
||||
Node zerop = Operation(OperationCode::LogicalFOrdEqual, std::move(value), Immediate(0.0f));
|
||||
SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop));
|
||||
LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete");
|
||||
}
|
||||
|
||||
@@ -150,18 +150,19 @@ public:
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
if (is_current) {
|
||||
return;
|
||||
// We can't track the current state of the underlying context in this wrapper class because
|
||||
// Qt may make the underlying context not current for one reason or another. In particular,
|
||||
// the WebBrowser uses GL, so it seems to conflict if we aren't careful.
|
||||
// Instead of always just making the context current (which does not have any caching to
|
||||
// check if the underlying context is already current) we can check for the current context
|
||||
// in the thread local data by calling `currentContext()` and checking if its ours.
|
||||
if (QOpenGLContext::currentContext() != context.get()) {
|
||||
context->makeCurrent(surface);
|
||||
}
|
||||
is_current = context->makeCurrent(surface);
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
if (!is_current) {
|
||||
return;
|
||||
}
|
||||
context->doneCurrent();
|
||||
is_current = false;
|
||||
}
|
||||
|
||||
QOpenGLContext* GetShareContext() {
|
||||
@@ -178,7 +179,6 @@ private:
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
|
||||
QSurface* surface;
|
||||
bool is_current = false;
|
||||
};
|
||||
|
||||
class DummyContext : public Core::Frontend::GraphicsContext {};
|
||||
|
||||
@@ -687,6 +687,8 @@ void Config::ReadSystemValues() {
|
||||
|
||||
Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt();
|
||||
|
||||
Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt();
|
||||
|
||||
const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool();
|
||||
if (rng_seed_enabled) {
|
||||
Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong();
|
||||
@@ -1126,6 +1128,7 @@ void Config::SaveSystemValues() {
|
||||
WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0);
|
||||
WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1);
|
||||
WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1);
|
||||
WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
|
||||
|
||||
WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false);
|
||||
WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0);
|
||||
|
||||
@@ -57,6 +57,7 @@ void ConfigureSystem::SetConfiguration() {
|
||||
|
||||
ui->combo_language->setCurrentIndex(Settings::values.language_index);
|
||||
ui->combo_region->setCurrentIndex(Settings::values.region_index);
|
||||
ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index);
|
||||
ui->combo_sound->setCurrentIndex(Settings::values.sound_index);
|
||||
|
||||
ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value());
|
||||
@@ -84,6 +85,7 @@ void ConfigureSystem::ApplyConfiguration() {
|
||||
|
||||
Settings::values.language_index = ui->combo_language->currentIndex();
|
||||
Settings::values.region_index = ui->combo_region->currentIndex();
|
||||
Settings::values.time_zone_index = ui->combo_time_zone->currentIndex();
|
||||
Settings::values.sound_index = ui->combo_sound->currentIndex();
|
||||
|
||||
if (ui->rng_seed_checkbox->isChecked()) {
|
||||
|
||||
@@ -37,5 +37,6 @@ private:
|
||||
|
||||
int language_index = 0;
|
||||
int region_index = 0;
|
||||
int time_zone_index = 0;
|
||||
int sound_index = 0;
|
||||
};
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
<string>System Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_sound">
|
||||
<property name="text">
|
||||
<string>Sound output mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_console_id">
|
||||
<property name="text">
|
||||
<string>Console ID:</string>
|
||||
@@ -174,14 +174,255 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_timezone">
|
||||
<property name="text">
|
||||
<string>Time Zone:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="combo_time_zone">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Auto</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CET</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CST6CDT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Cuba</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>EET</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Egypt</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Eire</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>EST</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>EST5EDT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>GB</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>GB-Eire</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>GMT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>GMT+0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>GMT-0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>GMT0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Greenwich</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Hongkong</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>HST</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Iceland</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Iran</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Israel</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Jamaica</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Japan</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Kwajalein</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Libya</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MET</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MST</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MST7MDT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Navajo</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>NZ</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>NZ-CHAT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Poland</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Portugal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PRC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PST8PDT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ROC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ROK</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Singapore</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Turkey</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>UCT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Universal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>UTC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>W-SU</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>WET</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Zulu</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="rng_seed_checkbox">
|
||||
<property name="text">
|
||||
<string>RNG Seed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="combo_sound">
|
||||
<item>
|
||||
<property name="text">
|
||||
@@ -207,7 +448,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QPushButton" name="button_regenerate_console_id">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
@@ -223,14 +464,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="custom_rtc_checkbox">
|
||||
<property name="text">
|
||||
<string>Custom RTC</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QDateTimeEdit" name="custom_rtc_edit">
|
||||
<property name="minimumDate">
|
||||
<date>
|
||||
@@ -244,7 +485,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="rng_seed_edit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
|
||||
@@ -488,11 +488,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string pat
|
||||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
|
||||
|
||||
connect(open_save_location, &QAction::triggered, [this, program_id]() {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData);
|
||||
connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
|
||||
emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
|
||||
});
|
||||
connect(open_lfs_location, &QAction::triggered, [this, program_id]() {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::ModData);
|
||||
connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() {
|
||||
emit OpenFolderRequested(GameListOpenTarget::ModData, path);
|
||||
});
|
||||
connect(open_transferable_shader_cache, &QAction::triggered,
|
||||
[this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
signals:
|
||||
void GameChosen(QString game_path);
|
||||
void ShouldCancelWorker();
|
||||
void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
|
||||
void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
|
||||
void OpenTransferableShaderCacheRequested(u64 program_id);
|
||||
void DumpRomFSRequested(u64 program_id, const std::string& game_path);
|
||||
void CopyTIDRequested(u64 program_id);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <QTime>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "ui_loading_screen.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
@@ -61,7 +62,7 @@ LoadingScreen::LoadingScreen(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()),
|
||||
previous_stage(VideoCore::LoadCallbackStage::Complete) {
|
||||
ui->setupUi(this);
|
||||
setMinimumSize(1280, 720);
|
||||
setMinimumSize(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
|
||||
|
||||
// Create a fade out effect to hide this loading screen widget.
|
||||
// When fading opacity, it will fade to the parent widgets background color, which is why we
|
||||
|
||||
@@ -135,6 +135,28 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
QString GetAccountUsername() {
|
||||
const QString nouser = QString::fromStdString("No User");
|
||||
Service::Account::ProfileManager manager;
|
||||
const auto current_user = manager.GetUser(Settings::values.current_user);
|
||||
if (!current_user.has_value() || (current_user == Common::UUID{})) {
|
||||
return nouser;
|
||||
}
|
||||
Service::Account::ProfileBase profile;
|
||||
if (!manager.GetProfileBase(*current_user, profile)) {
|
||||
return nouser;
|
||||
}
|
||||
|
||||
const auto text = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
|
||||
|
||||
return text.empty() ? nouser : QString::fromStdString(text);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
constexpr int default_mouse_timeout = 2500;
|
||||
|
||||
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
|
||||
@@ -493,6 +515,48 @@ void GMainWindow::InitializeWidgets() {
|
||||
statusBar()->addPermanentWidget(label);
|
||||
}
|
||||
|
||||
// Setup Profile button
|
||||
profile_status_button = new QPushButton();
|
||||
profile_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
|
||||
profile_status_button->setCheckable(true);
|
||||
profile_status_button->setChecked(true);
|
||||
profile_status_button->setFocusPolicy(Qt::NoFocus);
|
||||
const auto username = GetAccountUsername();
|
||||
profile_status_button->setText(username);
|
||||
connect(profile_status_button, &QPushButton::clicked, [=] {
|
||||
profile_status_button->setChecked(true);
|
||||
|
||||
if (emulation_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
// User save data
|
||||
const auto select_profile = [this] {
|
||||
QtProfileSelectionDialog dialog(this);
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
|
||||
if (dialog.exec() == QDialog::Rejected) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dialog.GetIndex();
|
||||
};
|
||||
|
||||
const auto index = select_profile();
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Settings::values.current_user = index;
|
||||
Settings::Apply();
|
||||
|
||||
const auto username = GetAccountUsername();
|
||||
profile_status_button->setText(username);
|
||||
});
|
||||
statusBar()->insertPermanentWidget(0, profile_status_button);
|
||||
|
||||
// Setup Dock button
|
||||
dock_status_button = new QPushButton();
|
||||
dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
|
||||
@@ -724,13 +788,13 @@ void GMainWindow::InitializeHotkeys() {
|
||||
}
|
||||
|
||||
void GMainWindow::SetDefaultUIGeometry() {
|
||||
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
|
||||
// geometry: 53% of the window contents are in the upper screen half, 47% in the lower half
|
||||
const QRect screenRect = QApplication::desktop()->screenGeometry(this);
|
||||
|
||||
const int w = screenRect.width() * 2 / 3;
|
||||
const int h = screenRect.height() / 2;
|
||||
const int h = screenRect.height() * 2 / 3;
|
||||
const int x = (screenRect.x() + screenRect.width()) / 2 - w / 2;
|
||||
const int y = (screenRect.y() + screenRect.height()) / 2 - h * 55 / 100;
|
||||
const int y = (screenRect.y() + screenRect.height()) / 2 - h * 53 / 100;
|
||||
|
||||
setGeometry(x, y, w, h);
|
||||
}
|
||||
@@ -831,6 +895,7 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
&GMainWindow::OnDisplayTitleBars);
|
||||
connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar);
|
||||
connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
|
||||
connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize);
|
||||
|
||||
// Fullscreen
|
||||
ui.action_Fullscreen->setShortcut(
|
||||
@@ -1154,40 +1219,62 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
|
||||
BootGame(game_path);
|
||||
}
|
||||
|
||||
void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target) {
|
||||
void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path) {
|
||||
std::string path;
|
||||
QString open_target;
|
||||
|
||||
const auto v_file = Core::GetGameFileFromPath(vfs, game_path);
|
||||
const auto loader = Loader::GetLoader(v_file);
|
||||
FileSys::NACP control{};
|
||||
u64 program_id{};
|
||||
|
||||
loader->ReadControlData(control);
|
||||
loader->ReadProgramId(program_id);
|
||||
|
||||
const bool has_user_save{control.GetDefaultNormalSaveSize() > 0};
|
||||
const bool has_device_save{control.GetDeviceSaveDataSize() > 0};
|
||||
|
||||
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
|
||||
|
||||
switch (target) {
|
||||
case GameListOpenTarget::SaveData: {
|
||||
open_target = tr("Save Data");
|
||||
const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
ASSERT(program_id != 0);
|
||||
|
||||
const auto select_profile = [this] {
|
||||
QtProfileSelectionDialog dialog(this);
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
if (has_user_save) {
|
||||
// User save data
|
||||
const auto select_profile = [this] {
|
||||
QtProfileSelectionDialog dialog(this);
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
|
||||
if (dialog.exec() == QDialog::Rejected) {
|
||||
return -1;
|
||||
if (dialog.exec() == QDialog::Rejected) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dialog.GetIndex();
|
||||
};
|
||||
|
||||
const auto index = select_profile();
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
return dialog.GetIndex();
|
||||
};
|
||||
|
||||
const auto index = select_profile();
|
||||
if (index == -1) {
|
||||
return;
|
||||
Service::Account::ProfileManager manager;
|
||||
const auto user_id = manager.GetUser(static_cast<std::size_t>(index));
|
||||
ASSERT(user_id);
|
||||
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(
|
||||
FileSys::SaveDataSpaceId::NandUser,
|
||||
FileSys::SaveDataType::SaveData, program_id, user_id->uuid, 0);
|
||||
} else {
|
||||
// Device save data
|
||||
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(
|
||||
FileSys::SaveDataSpaceId::NandUser,
|
||||
FileSys::SaveDataType::SaveData, program_id, {}, 0);
|
||||
}
|
||||
|
||||
Service::Account::ProfileManager manager;
|
||||
const auto user_id = manager.GetUser(static_cast<std::size_t>(index));
|
||||
ASSERT(user_id);
|
||||
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser,
|
||||
FileSys::SaveDataType::SaveData,
|
||||
program_id, user_id->uuid, 0);
|
||||
|
||||
if (!FileUtil::Exists(path)) {
|
||||
FileUtil::CreateFullPath(path);
|
||||
FileUtil::CreateDir(path);
|
||||
@@ -1829,6 +1916,20 @@ void GMainWindow::ToggleWindowMode() {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::ResetWindowSize() {
|
||||
const auto aspect_ratio = Layout::EmulationAspectRatio(
|
||||
static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio),
|
||||
static_cast<float>(Layout::ScreenUndocked::Height) / Layout::ScreenUndocked::Width);
|
||||
if (!ui.action_Single_Window_Mode->isChecked()) {
|
||||
render_window->resize(Layout::ScreenUndocked::Height / aspect_ratio,
|
||||
Layout::ScreenUndocked::Height);
|
||||
} else {
|
||||
resize(Layout::ScreenUndocked::Height / aspect_ratio,
|
||||
Layout::ScreenUndocked::Height + menuBar()->height() +
|
||||
(ui.action_Show_Status_Bar->isChecked() ? statusBar()->height() : 0));
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnConfigure() {
|
||||
const auto old_theme = UISettings::values.theme;
|
||||
const bool old_discord_presence = UISettings::values.enable_discord_presence;
|
||||
@@ -1865,6 +1966,8 @@ void GMainWindow::OnConfigure() {
|
||||
ui.centralwidget->setMouseTracking(false);
|
||||
}
|
||||
|
||||
const auto username = GetAccountUsername();
|
||||
profile_status_button->setText(username);
|
||||
dock_status_button->setChecked(Settings::values.use_docked_mode);
|
||||
async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
|
||||
#ifdef HAS_VULKAN
|
||||
|
||||
@@ -183,7 +183,7 @@ private slots:
|
||||
void OnMenuReportCompatibility();
|
||||
/// Called whenever a user selects a game in the game list widget.
|
||||
void OnGameListLoadFile(QString game_path);
|
||||
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
|
||||
void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
|
||||
void OnTransferableShaderCacheOpenFile(u64 program_id);
|
||||
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
|
||||
void OnGameListCopyTID(u64 program_id);
|
||||
@@ -208,6 +208,7 @@ private slots:
|
||||
void ShowFullscreen();
|
||||
void HideFullscreen();
|
||||
void ToggleWindowMode();
|
||||
void ResetWindowSize();
|
||||
void OnCaptureScreenshot();
|
||||
void OnCoreError(Core::System::ResultStatus, std::string);
|
||||
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
|
||||
@@ -232,6 +233,7 @@ private:
|
||||
QLabel* emu_speed_label = nullptr;
|
||||
QLabel* game_fps_label = nullptr;
|
||||
QLabel* emu_frametime_label = nullptr;
|
||||
QPushButton* profile_status_button = nullptr;
|
||||
QPushButton* async_status_button = nullptr;
|
||||
QPushButton* renderer_status_button = nullptr;
|
||||
QPushButton* dock_status_button = nullptr;
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1081</width>
|
||||
<height>730</height>
|
||||
<width>1280</width>
|
||||
<height>720</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -44,7 +44,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1081</width>
|
||||
<width>1280</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -96,6 +96,7 @@
|
||||
<addaction name="action_Display_Dock_Widget_Headers"/>
|
||||
<addaction name="action_Show_Filter_Bar"/>
|
||||
<addaction name="action_Show_Status_Bar"/>
|
||||
<addaction name="action_Reset_Window_Size"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menu_View_Debugging"/>
|
||||
</widget>
|
||||
@@ -215,6 +216,11 @@
|
||||
<string>Show Status Bar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Reset_Window_Size">
|
||||
<property name="text">
|
||||
<string>Reset Window Size</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Fullscreen">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -367,6 +367,9 @@ void Config::ReadValues() {
|
||||
Settings::values.custom_rtc = std::nullopt;
|
||||
}
|
||||
|
||||
Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
|
||||
Settings::values.time_zone_index = sdl2_config->GetInteger("System", "time_zone_index", 0);
|
||||
|
||||
// Core
|
||||
Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
|
||||
|
||||
@@ -409,8 +412,6 @@ void Config::ReadValues() {
|
||||
Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
|
||||
Settings::values.volume = static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1));
|
||||
|
||||
Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
|
||||
|
||||
// Miscellaneous
|
||||
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
|
||||
Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
|
||||
|
||||
@@ -262,6 +262,10 @@ language_index =
|
||||
# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
|
||||
region_value =
|
||||
|
||||
# The system time zone that yuzu will use during emulation
|
||||
# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
|
||||
time_zone_index =
|
||||
|
||||
[Miscellaneous]
|
||||
# A filter which removes logs below a certain logging level.
|
||||
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
|
||||
|
||||
Reference in New Issue
Block a user