forked from etc/pineapple-src
early-access version 3040
This commit is contained in:
parent
b070369939
commit
bd29e6b1a8
33 changed files with 898 additions and 386 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 3039.
|
This is the source code for early-access 3040.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -98,9 +98,8 @@ System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
||||||
: core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {}
|
: core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {}
|
||||||
|
|
||||||
Result System::Initialize(const AudioRendererParameterInternal& params,
|
Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||||
Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size,
|
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||||
const u32 process_handle_, const u64 applet_resource_user_id_,
|
u32 process_handle_, u64 applet_resource_user_id_, s32 session_id_) {
|
||||||
const s32 session_id_) {
|
|
||||||
if (!CheckValidRevision(params.revision)) {
|
if (!CheckValidRevision(params.revision)) {
|
||||||
return Service::Audio::ERR_INVALID_REVISION;
|
return Service::Audio::ERR_INVALID_REVISION;
|
||||||
}
|
}
|
||||||
|
@ -354,6 +353,8 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||||
|
|
||||||
render_time_limit_percent = 100;
|
render_time_limit_percent = 100;
|
||||||
drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto;
|
drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto;
|
||||||
|
drop_voice_param = 1.0f;
|
||||||
|
num_voices_dropped = 0;
|
||||||
|
|
||||||
allocator.Align(0x40);
|
allocator.Align(0x40);
|
||||||
command_workbuffer_size = allocator.GetRemainingSize();
|
command_workbuffer_size = allocator.GetRemainingSize();
|
||||||
|
@ -547,7 +548,7 @@ u32 System::GetRenderingTimeLimit() const {
|
||||||
return render_time_limit_percent;
|
return render_time_limit_percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SetRenderingTimeLimit(const u32 limit) {
|
void System::SetRenderingTimeLimit(u32 limit) {
|
||||||
render_time_limit_percent = limit;
|
render_time_limit_percent = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,7 +636,7 @@ void System::SendCommandToDsp() {
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
||||||
[[maybe_unused]] const u64 command_buffer_size_) {
|
[[maybe_unused]] u64 command_buffer_size_) {
|
||||||
PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count);
|
PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count);
|
||||||
const auto start_time{core.CoreTiming().GetClockTicks()};
|
const auto start_time{core.CoreTiming().GetClockTicks()};
|
||||||
|
|
||||||
|
@ -693,7 +694,8 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
||||||
|
|
||||||
voice_context.SortInfo();
|
voice_context.SortInfo();
|
||||||
|
|
||||||
const auto start_estimated_time{command_buffer.estimated_process_time};
|
const auto start_estimated_time{drop_voice_param *
|
||||||
|
static_cast<f32>(command_buffer.estimated_process_time)};
|
||||||
|
|
||||||
command_generator.GenerateVoiceCommands();
|
command_generator.GenerateVoiceCommands();
|
||||||
command_generator.GenerateSubMixCommands();
|
command_generator.GenerateSubMixCommands();
|
||||||
|
@ -712,11 +714,16 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
||||||
render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported();
|
render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported();
|
||||||
time_limit_percent = 70.0f;
|
time_limit_percent = 70.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto end_estimated_time{drop_voice_param *
|
||||||
|
static_cast<f32>(command_buffer.estimated_process_time)};
|
||||||
|
const auto estimated_time{start_estimated_time - end_estimated_time};
|
||||||
|
|
||||||
const auto time_limit{static_cast<u32>(
|
const auto time_limit{static_cast<u32>(
|
||||||
static_cast<f32>(start_estimated_time - command_buffer.estimated_process_time) +
|
estimated_time + (((time_limit_percent / 100.0f) * 2'880'000.0) *
|
||||||
(((time_limit_percent / 100.0f) * 2'880'000.0) *
|
(static_cast<f32>(render_time_limit_percent) / 100.0f)))};
|
||||||
(static_cast<f32>(render_time_limit_percent) / 100.0f)))};
|
num_voices_dropped =
|
||||||
num_voices_dropped = DropVoices(command_buffer, start_estimated_time, time_limit);
|
DropVoices(command_buffer, static_cast<u32>(start_estimated_time), time_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
command_list_header->buffer_size = command_buffer.size;
|
command_list_header->buffer_size = command_buffer.size;
|
||||||
|
@ -737,24 +744,33 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
||||||
return command_buffer.size;
|
return command_buffer.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_process_time,
|
f32 System::GetVoiceDropParameter() const {
|
||||||
const u32 time_limit) {
|
return drop_voice_param;
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::SetVoiceDropParameter(f32 voice_drop_) {
|
||||||
|
drop_voice_param = voice_drop_;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit) {
|
||||||
u32 i{0};
|
u32 i{0};
|
||||||
auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)};
|
auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)};
|
||||||
ICommand* cmd{};
|
ICommand* cmd{nullptr};
|
||||||
|
|
||||||
for (; i < command_buffer.count; i++) {
|
// Find a first valid voice to drop
|
||||||
|
while (i < command_buffer.count) {
|
||||||
cmd = reinterpret_cast<ICommand*>(command_list);
|
cmd = reinterpret_cast<ICommand*>(command_list);
|
||||||
if (cmd->type != CommandId::Performance &&
|
if (cmd->type == CommandId::Performance ||
|
||||||
cmd->type != CommandId::DataSourcePcmInt16Version1 &&
|
cmd->type == CommandId::DataSourcePcmInt16Version1 ||
|
||||||
cmd->type != CommandId::DataSourcePcmInt16Version2 &&
|
cmd->type == CommandId::DataSourcePcmInt16Version2 ||
|
||||||
cmd->type != CommandId::DataSourcePcmFloatVersion1 &&
|
cmd->type == CommandId::DataSourcePcmFloatVersion1 ||
|
||||||
cmd->type != CommandId::DataSourcePcmFloatVersion2 &&
|
cmd->type == CommandId::DataSourcePcmFloatVersion2 ||
|
||||||
cmd->type != CommandId::DataSourceAdpcmVersion1 &&
|
cmd->type == CommandId::DataSourceAdpcmVersion1 ||
|
||||||
cmd->type != CommandId::DataSourceAdpcmVersion2) {
|
cmd->type == CommandId::DataSourceAdpcmVersion2) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
command_list += cmd->size;
|
command_list += cmd->size;
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) {
|
if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) {
|
||||||
|
@ -767,6 +783,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
|
||||||
const auto node_id_type{cmd->node_id >> 28};
|
const auto node_id_type{cmd->node_id >> 28};
|
||||||
const auto node_id_base{cmd->node_id & 0xFFF};
|
const auto node_id_base{cmd->node_id & 0xFFF};
|
||||||
|
|
||||||
|
// If the new estimated process time falls below the limit, we're done dropping.
|
||||||
if (estimated_process_time <= time_limit) {
|
if (estimated_process_time <= time_limit) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -775,6 +792,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't drop voices marked with the highest priority.
|
||||||
auto& voice_info{voice_context.GetInfo(node_id_base)};
|
auto& voice_info{voice_context.GetInfo(node_id_base)};
|
||||||
if (voice_info.priority == HighestVoicePriority) {
|
if (voice_info.priority == HighestVoicePriority) {
|
||||||
break;
|
break;
|
||||||
|
@ -783,18 +801,23 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
|
||||||
voices_dropped++;
|
voices_dropped++;
|
||||||
voice_info.voice_dropped = true;
|
voice_info.voice_dropped = true;
|
||||||
|
|
||||||
if (i < command_buffer.count) {
|
// First iteration should drop the voice, and then iterate through all of the commands tied
|
||||||
while (cmd->node_id == node_id) {
|
// to the voice. We don't need reverb on a voice which we've just removed, for example.
|
||||||
if (cmd->type == CommandId::DepopPrepare) {
|
// Depops can't be removed otherwise we'll introduce audio popping, and we don't
|
||||||
cmd->enabled = true;
|
// remove perf commands. Lower the estimated time for each command dropped.
|
||||||
} else if (cmd->type == CommandId::Performance || !cmd->enabled) {
|
while (i < command_buffer.count && cmd->node_id == node_id) {
|
||||||
cmd->enabled = false;
|
if (cmd->type == CommandId::DepopPrepare) {
|
||||||
}
|
cmd->enabled = true;
|
||||||
i++;
|
} else if (cmd->enabled && cmd->type != CommandId::Performance) {
|
||||||
command_list += cmd->size;
|
cmd->enabled = false;
|
||||||
cmd = reinterpret_cast<ICommand*>(command_list);
|
estimated_process_time -= static_cast<u32>(
|
||||||
|
drop_voice_param * static_cast<f32>(cmd->estimated_process_time));
|
||||||
}
|
}
|
||||||
|
command_list += cmd->size;
|
||||||
|
cmd = reinterpret_cast<ICommand*>(command_list);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
return voices_dropped;
|
return voices_dropped;
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,6 +196,20 @@ public:
|
||||||
*/
|
*/
|
||||||
u32 DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit);
|
u32 DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current voice drop parameter.
|
||||||
|
*
|
||||||
|
* @return The current voice drop.
|
||||||
|
*/
|
||||||
|
f32 GetVoiceDropParameter() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the voice drop parameter.
|
||||||
|
*
|
||||||
|
* @param The new voice drop.
|
||||||
|
*/
|
||||||
|
void SetVoiceDropParameter(f32 voice_drop);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& core;
|
Core::System& core;
|
||||||
|
@ -301,6 +315,8 @@ private:
|
||||||
u32 num_voices_dropped{};
|
u32 num_voices_dropped{};
|
||||||
/// Tick that rendering started
|
/// Tick that rendering started
|
||||||
u64 render_start_tick{};
|
u64 render_start_tick{};
|
||||||
|
/// Parameter to control the threshold for dropping voices if the audio graph gets too large
|
||||||
|
f32 drop_voice_param{1.0f};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioRenderer
|
} // namespace AudioRenderer
|
||||||
|
|
|
@ -74,8 +74,8 @@ void VoiceContext::SortInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) {
|
std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) {
|
||||||
return a->priority != b->priority ? a->priority < b->priority
|
return a->priority != b->priority ? a->priority > b->priority
|
||||||
: a->sort_order < b->sort_order;
|
: a->sort_order > b->sort_order;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,4 +34,12 @@ concept DerivedFrom = requires {
|
||||||
template <typename From, typename To>
|
template <typename From, typename To>
|
||||||
concept ConvertibleTo = std::is_convertible_v<From, To>;
|
concept ConvertibleTo = std::is_convertible_v<From, To>;
|
||||||
|
|
||||||
|
// No equivalents in the stdlib
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept IsArithmetic = std::is_arithmetic_v<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept IsIntegral = std::is_integral_v<T>;
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <common/concepts.h>
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
template <size_t I, size_t F>
|
template <size_t I, size_t F>
|
||||||
|
@ -50,8 +52,8 @@ struct type_from_size<64> {
|
||||||
static constexpr size_t size = 64;
|
static constexpr size_t size = 64;
|
||||||
|
|
||||||
using value_type = int64_t;
|
using value_type = int64_t;
|
||||||
using unsigned_type = std::make_unsigned<value_type>::type;
|
using unsigned_type = std::make_unsigned_t<value_type>;
|
||||||
using signed_type = std::make_signed<value_type>::type;
|
using signed_type = std::make_signed_t<value_type>;
|
||||||
using next_size = type_from_size<128>;
|
using next_size = type_from_size<128>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,8 +63,8 @@ struct type_from_size<32> {
|
||||||
static constexpr size_t size = 32;
|
static constexpr size_t size = 32;
|
||||||
|
|
||||||
using value_type = int32_t;
|
using value_type = int32_t;
|
||||||
using unsigned_type = std::make_unsigned<value_type>::type;
|
using unsigned_type = std::make_unsigned_t<value_type>;
|
||||||
using signed_type = std::make_signed<value_type>::type;
|
using signed_type = std::make_signed_t<value_type>;
|
||||||
using next_size = type_from_size<64>;
|
using next_size = type_from_size<64>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,8 +74,8 @@ struct type_from_size<16> {
|
||||||
static constexpr size_t size = 16;
|
static constexpr size_t size = 16;
|
||||||
|
|
||||||
using value_type = int16_t;
|
using value_type = int16_t;
|
||||||
using unsigned_type = std::make_unsigned<value_type>::type;
|
using unsigned_type = std::make_unsigned_t<value_type>;
|
||||||
using signed_type = std::make_signed<value_type>::type;
|
using signed_type = std::make_signed_t<value_type>;
|
||||||
using next_size = type_from_size<32>;
|
using next_size = type_from_size<32>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,8 +85,8 @@ struct type_from_size<8> {
|
||||||
static constexpr size_t size = 8;
|
static constexpr size_t size = 8;
|
||||||
|
|
||||||
using value_type = int8_t;
|
using value_type = int8_t;
|
||||||
using unsigned_type = std::make_unsigned<value_type>::type;
|
using unsigned_type = std::make_unsigned_t<value_type>;
|
||||||
using signed_type = std::make_signed<value_type>::type;
|
using signed_type = std::make_signed_t<value_type>;
|
||||||
using next_size = type_from_size<16>;
|
using next_size = type_from_size<16>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,7 +103,7 @@ struct divide_by_zero : std::exception {};
|
||||||
template <size_t I, size_t F>
|
template <size_t I, size_t F>
|
||||||
constexpr FixedPoint<I, F> divide(
|
constexpr FixedPoint<I, F> divide(
|
||||||
FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
|
FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
|
||||||
typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
|
std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
|
||||||
|
|
||||||
using next_type = typename FixedPoint<I, F>::next_type;
|
using next_type = typename FixedPoint<I, F>::next_type;
|
||||||
using base_type = typename FixedPoint<I, F>::base_type;
|
using base_type = typename FixedPoint<I, F>::base_type;
|
||||||
|
@ -121,7 +123,7 @@ constexpr FixedPoint<I, F> divide(
|
||||||
template <size_t I, size_t F>
|
template <size_t I, size_t F>
|
||||||
constexpr FixedPoint<I, F> divide(
|
constexpr FixedPoint<I, F> divide(
|
||||||
FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
|
FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
|
||||||
typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
|
std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
|
||||||
|
|
||||||
using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
|
using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
|
||||||
|
|
||||||
|
@ -191,7 +193,7 @@ constexpr FixedPoint<I, F> divide(
|
||||||
template <size_t I, size_t F>
|
template <size_t I, size_t F>
|
||||||
constexpr FixedPoint<I, F> multiply(
|
constexpr FixedPoint<I, F> multiply(
|
||||||
FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
|
FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
|
||||||
typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
|
std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
|
||||||
|
|
||||||
using next_type = typename FixedPoint<I, F>::next_type;
|
using next_type = typename FixedPoint<I, F>::next_type;
|
||||||
using base_type = typename FixedPoint<I, F>::base_type;
|
using base_type = typename FixedPoint<I, F>::base_type;
|
||||||
|
@ -210,7 +212,7 @@ constexpr FixedPoint<I, F> multiply(
|
||||||
template <size_t I, size_t F>
|
template <size_t I, size_t F>
|
||||||
constexpr FixedPoint<I, F> multiply(
|
constexpr FixedPoint<I, F> multiply(
|
||||||
FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
|
FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
|
||||||
typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
|
std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
|
||||||
|
|
||||||
using base_type = typename FixedPoint<I, F>::base_type;
|
using base_type = typename FixedPoint<I, F>::base_type;
|
||||||
|
|
||||||
|
@ -265,15 +267,16 @@ public:
|
||||||
static constexpr base_type one = base_type(1) << fractional_bits;
|
static constexpr base_type one = base_type(1) << fractional_bits;
|
||||||
|
|
||||||
public: // constructors
|
public: // constructors
|
||||||
FixedPoint() = default;
|
constexpr FixedPoint() = default;
|
||||||
FixedPoint(const FixedPoint&) = default;
|
|
||||||
FixedPoint(FixedPoint&&) = default;
|
|
||||||
FixedPoint& operator=(const FixedPoint&) = default;
|
|
||||||
|
|
||||||
template <class Number>
|
constexpr FixedPoint(const FixedPoint&) = default;
|
||||||
constexpr FixedPoint(
|
constexpr FixedPoint& operator=(const FixedPoint&) = default;
|
||||||
Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr)
|
|
||||||
: data_(static_cast<base_type>(n * one)) {}
|
constexpr FixedPoint(FixedPoint&&) noexcept = default;
|
||||||
|
constexpr FixedPoint& operator=(FixedPoint&&) noexcept = default;
|
||||||
|
|
||||||
|
template <IsArithmetic Number>
|
||||||
|
constexpr FixedPoint(Number n) : data_(static_cast<base_type>(n * one)) {}
|
||||||
|
|
||||||
public: // conversion
|
public: // conversion
|
||||||
template <size_t I2, size_t F2>
|
template <size_t I2, size_t F2>
|
||||||
|
@ -301,36 +304,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
public: // comparison operators
|
public: // comparison operators
|
||||||
constexpr bool operator==(FixedPoint rhs) const {
|
friend constexpr auto operator<=>(FixedPoint lhs, FixedPoint rhs) = default;
|
||||||
return data_ == rhs.data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator!=(FixedPoint rhs) const {
|
|
||||||
return data_ != rhs.data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator<(FixedPoint rhs) const {
|
|
||||||
return data_ < rhs.data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator>(FixedPoint rhs) const {
|
|
||||||
return data_ > rhs.data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator<=(FixedPoint rhs) const {
|
|
||||||
return data_ <= rhs.data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator>=(FixedPoint rhs) const {
|
|
||||||
return data_ >= rhs.data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
public: // unary operators
|
public: // unary operators
|
||||||
constexpr bool operator!() const {
|
[[nodiscard]] constexpr bool operator!() const {
|
||||||
return !data_;
|
return !data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr FixedPoint operator~() const {
|
[[nodiscard]] constexpr FixedPoint operator~() const {
|
||||||
// NOTE(eteran): this will often appear to "just negate" the value
|
// NOTE(eteran): this will often appear to "just negate" the value
|
||||||
// that is not an error, it is because -x == (~x+1)
|
// that is not an error, it is because -x == (~x+1)
|
||||||
// and that "+1" is adding an infinitesimally small fraction to the
|
// and that "+1" is adding an infinitesimally small fraction to the
|
||||||
|
@ -338,11 +319,11 @@ public: // unary operators
|
||||||
return FixedPoint::from_base(~data_);
|
return FixedPoint::from_base(~data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr FixedPoint operator-() const {
|
[[nodiscard]] constexpr FixedPoint operator-() const {
|
||||||
return FixedPoint::from_base(-data_);
|
return FixedPoint::from_base(-data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr FixedPoint operator+() const {
|
[[nodiscard]] constexpr FixedPoint operator+() const {
|
||||||
return FixedPoint::from_base(+data_);
|
return FixedPoint::from_base(+data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,15 +392,13 @@ public: // binary math operators, effects underlying bit pattern since these
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Integer,
|
template <IsIntegral Integer>
|
||||||
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
|
|
||||||
constexpr FixedPoint& operator>>=(Integer n) {
|
constexpr FixedPoint& operator>>=(Integer n) {
|
||||||
data_ >>= n;
|
data_ >>= n;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Integer,
|
template <IsIntegral Integer>
|
||||||
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
|
|
||||||
constexpr FixedPoint& operator<<=(Integer n) {
|
constexpr FixedPoint& operator<<=(Integer n) {
|
||||||
data_ <<= n;
|
data_ <<= n;
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -430,42 +409,42 @@ public: // conversion to basic types
|
||||||
data_ += (data_ & fractional_mask) >> 1;
|
data_ += (data_ & fractional_mask) >> 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int to_int() {
|
[[nodiscard]] constexpr int to_int() {
|
||||||
round_up();
|
round_up();
|
||||||
return static_cast<int>((data_ & integer_mask) >> fractional_bits);
|
return static_cast<int>((data_ & integer_mask) >> fractional_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr unsigned int to_uint() const {
|
[[nodiscard]] constexpr unsigned int to_uint() {
|
||||||
round_up();
|
round_up();
|
||||||
return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
|
return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int64_t to_long() {
|
[[nodiscard]] constexpr int64_t to_long() {
|
||||||
round_up();
|
round_up();
|
||||||
return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
|
return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int to_int_floor() const {
|
[[nodiscard]] constexpr int to_int_floor() const {
|
||||||
return static_cast<int>((data_ & integer_mask) >> fractional_bits);
|
return static_cast<int>((data_ & integer_mask) >> fractional_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int64_t to_long_floor() {
|
[[nodiscard]] constexpr int64_t to_long_floor() const {
|
||||||
return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
|
return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr unsigned int to_uint_floor() const {
|
[[nodiscard]] constexpr unsigned int to_uint_floor() const {
|
||||||
return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
|
return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr float to_float() const {
|
[[nodiscard]] constexpr float to_float() const {
|
||||||
return static_cast<float>(data_) / FixedPoint::one;
|
return static_cast<float>(data_) / FixedPoint::one;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr double to_double() const {
|
[[nodiscard]] constexpr double to_double() const {
|
||||||
return static_cast<double>(data_) / FixedPoint::one;
|
return static_cast<double>(data_) / FixedPoint::one;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr base_type to_raw() const {
|
[[nodiscard]] constexpr base_type to_raw() const {
|
||||||
return data_;
|
return data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,27 +452,27 @@ public: // conversion to basic types
|
||||||
data_ &= fractional_mask;
|
data_ &= fractional_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr base_type get_frac() const {
|
[[nodiscard]] constexpr base_type get_frac() const {
|
||||||
return data_ & fractional_mask;
|
return data_ & fractional_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr void swap(FixedPoint& rhs) {
|
constexpr void swap(FixedPoint& rhs) noexcept {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(data_, rhs.data_);
|
swap(data_, rhs.data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
base_type data_;
|
base_type data_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// if we have the same fractional portion, but differing integer portions, we trivially upgrade the
|
// if we have the same fractional portion, but differing integer portions, we trivially upgrade the
|
||||||
// smaller type
|
// smaller type
|
||||||
template <size_t I1, size_t I2, size_t F>
|
template <size_t I1, size_t I2, size_t F>
|
||||||
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator+(
|
constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator+(
|
||||||
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||||
|
|
||||||
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
|
using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
|
||||||
|
|
||||||
const T l = T::from_base(lhs.to_raw());
|
const T l = T::from_base(lhs.to_raw());
|
||||||
const T r = T::from_base(rhs.to_raw());
|
const T r = T::from_base(rhs.to_raw());
|
||||||
|
@ -501,10 +480,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t I1, size_t I2, size_t F>
|
template <size_t I1, size_t I2, size_t F>
|
||||||
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator-(
|
constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator-(
|
||||||
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||||
|
|
||||||
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
|
using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
|
||||||
|
|
||||||
const T l = T::from_base(lhs.to_raw());
|
const T l = T::from_base(lhs.to_raw());
|
||||||
const T r = T::from_base(rhs.to_raw());
|
const T r = T::from_base(rhs.to_raw());
|
||||||
|
@ -512,10 +491,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t I1, size_t I2, size_t F>
|
template <size_t I1, size_t I2, size_t F>
|
||||||
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator*(
|
constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator*(
|
||||||
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||||
|
|
||||||
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
|
using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
|
||||||
|
|
||||||
const T l = T::from_base(lhs.to_raw());
|
const T l = T::from_base(lhs.to_raw());
|
||||||
const T r = T::from_base(rhs.to_raw());
|
const T r = T::from_base(rhs.to_raw());
|
||||||
|
@ -523,10 +502,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t I1, size_t I2, size_t F>
|
template <size_t I1, size_t I2, size_t F>
|
||||||
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator/(
|
constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator/(
|
||||||
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||||
|
|
||||||
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
|
using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
|
||||||
|
|
||||||
const T l = T::from_base(lhs.to_raw());
|
const T l = T::from_base(lhs.to_raw());
|
||||||
const T r = T::from_base(rhs.to_raw());
|
const T r = T::from_base(rhs.to_raw());
|
||||||
|
@ -561,54 +540,46 @@ constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs)
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
|
constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
|
||||||
lhs += FixedPoint<I, F>(rhs);
|
lhs += FixedPoint<I, F>(rhs);
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
|
constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
|
||||||
lhs -= FixedPoint<I, F>(rhs);
|
lhs -= FixedPoint<I, F>(rhs);
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
|
constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
|
||||||
lhs *= FixedPoint<I, F>(rhs);
|
lhs *= FixedPoint<I, F>(rhs);
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
|
constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
|
||||||
lhs /= FixedPoint<I, F>(rhs);
|
lhs /= FixedPoint<I, F>(rhs);
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
|
constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
FixedPoint<I, F> tmp(lhs);
|
FixedPoint<I, F> tmp(lhs);
|
||||||
tmp += rhs;
|
tmp += rhs;
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
|
constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
FixedPoint<I, F> tmp(lhs);
|
FixedPoint<I, F> tmp(lhs);
|
||||||
tmp -= rhs;
|
tmp -= rhs;
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
|
constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
FixedPoint<I, F> tmp(lhs);
|
FixedPoint<I, F> tmp(lhs);
|
||||||
tmp *= rhs;
|
tmp *= rhs;
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
|
constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
FixedPoint<I, F> tmp(lhs);
|
FixedPoint<I, F> tmp(lhs);
|
||||||
tmp /= rhs;
|
tmp /= rhs;
|
||||||
|
@ -616,78 +587,64 @@ constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// shift operators
|
// shift operators
|
||||||
template <size_t I, size_t F, class Integer,
|
template <size_t I, size_t F, IsIntegral Integer>
|
||||||
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
|
|
||||||
constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
|
constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
|
||||||
lhs <<= rhs;
|
lhs <<= rhs;
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Integer,
|
template <size_t I, size_t F, IsIntegral Integer>
|
||||||
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
|
|
||||||
constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
|
constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
|
||||||
lhs >>= rhs;
|
lhs >>= rhs;
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// comparison operators
|
// comparison operators
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
|
constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
|
||||||
return lhs > FixedPoint<I, F>(rhs);
|
return lhs > FixedPoint<I, F>(rhs);
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
|
constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
|
||||||
return lhs < FixedPoint<I, F>(rhs);
|
return lhs < FixedPoint<I, F>(rhs);
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
|
constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
|
||||||
return lhs >= FixedPoint<I, F>(rhs);
|
return lhs >= FixedPoint<I, F>(rhs);
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
|
constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
|
||||||
return lhs <= FixedPoint<I, F>(rhs);
|
return lhs <= FixedPoint<I, F>(rhs);
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
|
constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
|
||||||
return lhs == FixedPoint<I, F>(rhs);
|
return lhs == FixedPoint<I, F>(rhs);
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
|
constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
|
||||||
return lhs != FixedPoint<I, F>(rhs);
|
return lhs != FixedPoint<I, F>(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
|
constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
return FixedPoint<I, F>(lhs) > rhs;
|
return FixedPoint<I, F>(lhs) > rhs;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
|
constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
return FixedPoint<I, F>(lhs) < rhs;
|
return FixedPoint<I, F>(lhs) < rhs;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
|
constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
return FixedPoint<I, F>(lhs) >= rhs;
|
return FixedPoint<I, F>(lhs) >= rhs;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
|
constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
return FixedPoint<I, F>(lhs) <= rhs;
|
return FixedPoint<I, F>(lhs) <= rhs;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
|
constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
return FixedPoint<I, F>(lhs) == rhs;
|
return FixedPoint<I, F>(lhs) == rhs;
|
||||||
}
|
}
|
||||||
template <size_t I, size_t F, class Number,
|
template <size_t I, size_t F, IsArithmetic Number>
|
||||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
|
||||||
constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
|
constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
|
||||||
return FixedPoint<I, F>(lhs) != rhs;
|
return FixedPoint<I, F>(lhs) != rhs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,6 +243,8 @@ add_library(core STATIC
|
||||||
hle/kernel/k_server_session.h
|
hle/kernel/k_server_session.h
|
||||||
hle/kernel/k_session.cpp
|
hle/kernel/k_session.cpp
|
||||||
hle/kernel/k_session.h
|
hle/kernel/k_session.h
|
||||||
|
hle/kernel/k_session_request.cpp
|
||||||
|
hle/kernel/k_session_request.h
|
||||||
hle/kernel/k_shared_memory.cpp
|
hle/kernel/k_shared_memory.cpp
|
||||||
hle/kernel/k_shared_memory.h
|
hle/kernel/k_shared_memory.h
|
||||||
hle/kernel/k_shared_memory_info.h
|
hle/kernel/k_shared_memory_info.h
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/uuid.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/savedata_factory.h"
|
#include "core/file_sys/savedata_factory.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs.h"
|
||||||
|
@ -59,6 +60,36 @@ bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataA
|
||||||
attr.title_id == 0 && attr.save_id == 0);
|
attr.title_id == 0 && attr.save_id == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u64 title_id,
|
||||||
|
u128 user_id) {
|
||||||
|
// Only detect nand user saves.
|
||||||
|
const auto space_id_path = [space_id]() -> std::string_view {
|
||||||
|
switch (space_id) {
|
||||||
|
case SaveDataSpaceId::NandUser:
|
||||||
|
return "/user/save";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (space_id_path.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::UUID uuid;
|
||||||
|
std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID));
|
||||||
|
|
||||||
|
// Only detect account/device saves from the future location.
|
||||||
|
switch (type) {
|
||||||
|
case SaveDataType::SaveData:
|
||||||
|
return fmt::format("{}/account/{}/{:016X}/1", space_id_path, uuid.RawString(), title_id);
|
||||||
|
case SaveDataType::DeviceSaveData:
|
||||||
|
return fmt::format("{}/device/{:016X}/1", space_id_path, title_id);
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
std::string SaveDataAttribute::DebugInfo() const {
|
std::string SaveDataAttribute::DebugInfo() const {
|
||||||
|
@ -82,7 +113,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
|
||||||
PrintSaveDataAttributeWarnings(meta);
|
PrintSaveDataAttributeWarnings(meta);
|
||||||
|
|
||||||
const auto save_directory =
|
const auto save_directory =
|
||||||
GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||||
|
|
||||||
auto out = dir->CreateDirectoryRelative(save_directory);
|
auto out = dir->CreateDirectoryRelative(save_directory);
|
||||||
|
|
||||||
|
@ -99,7 +130,7 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
|
||||||
const SaveDataAttribute& meta) const {
|
const SaveDataAttribute& meta) const {
|
||||||
|
|
||||||
const auto save_directory =
|
const auto save_directory =
|
||||||
GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||||
|
|
||||||
auto out = dir->GetDirectoryRelative(save_directory);
|
auto out = dir->GetDirectoryRelative(save_directory);
|
||||||
|
|
||||||
|
@ -134,9 +165,9 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId space,
|
std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir,
|
||||||
SaveDataType type, u64 title_id, u128 user_id,
|
SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
||||||
u64 save_id) {
|
u128 user_id, u64 save_id) {
|
||||||
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
|
// 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.
|
// be interpreted as the title id of the current process.
|
||||||
if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
|
if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
|
||||||
|
@ -145,6 +176,17 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For compat with a future impl.
|
||||||
|
if (std::string future_path =
|
||||||
|
GetFutureSaveDataPath(space, type, title_id & ~(0xFFULL), user_id);
|
||||||
|
!future_path.empty()) {
|
||||||
|
// Check if this location exists, and prefer it over the old.
|
||||||
|
if (const auto future_dir = dir->GetDirectoryRelative(future_path); future_dir != nullptr) {
|
||||||
|
LOG_INFO(Service_FS, "Using save at new location: {}", future_path);
|
||||||
|
return future_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string out = GetSaveDataSpaceIdPath(space);
|
std::string out = GetSaveDataSpaceIdPath(space);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -167,7 +209,8 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s
|
||||||
|
|
||||||
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||||
u128 user_id) const {
|
u128 user_id) const {
|
||||||
const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
const auto path =
|
||||||
|
GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||||
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
||||||
|
|
||||||
const auto size_file = relative_dir->GetFile(SAVE_DATA_SIZE_FILENAME);
|
const auto size_file = relative_dir->GetFile(SAVE_DATA_SIZE_FILENAME);
|
||||||
|
@ -185,7 +228,8 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||||
|
|
||||||
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||||
SaveDataSize new_value) const {
|
SaveDataSize new_value) const {
|
||||||
const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
const auto path =
|
||||||
|
GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||||
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
||||||
|
|
||||||
const auto size_file = relative_dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
|
const auto size_file = relative_dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
|
||||||
|
|
|
@ -95,8 +95,8 @@ public:
|
||||||
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
|
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
|
||||||
|
|
||||||
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
|
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
|
||||||
static std::string GetFullPath(Core::System& system, SaveDataSpaceId space, SaveDataType type,
|
static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space,
|
||||||
u64 title_id, u128 user_id, u64 save_id);
|
SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
|
||||||
|
|
||||||
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
|
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
|
||||||
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||||
|
|
|
@ -86,13 +86,13 @@ public:
|
||||||
u32 num_domain_objects{};
|
u32 num_domain_objects{};
|
||||||
const bool always_move_handles{
|
const bool always_move_handles{
|
||||||
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
|
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
|
||||||
if (!ctx.Session()->IsDomain() || always_move_handles) {
|
if (!ctx.Session()->GetSessionRequestManager()->IsDomain() || always_move_handles) {
|
||||||
num_handles_to_move = num_objects_to_move;
|
num_handles_to_move = num_objects_to_move;
|
||||||
} else {
|
} else {
|
||||||
num_domain_objects = num_objects_to_move;
|
num_domain_objects = num_objects_to_move;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.Session()->IsDomain()) {
|
if (ctx.Session()->GetSessionRequestManager()->IsDomain()) {
|
||||||
raw_data_size +=
|
raw_data_size +=
|
||||||
static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
|
static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
|
||||||
ctx.write_size += num_domain_objects;
|
ctx.write_size += num_domain_objects;
|
||||||
|
@ -125,7 +125,8 @@ public:
|
||||||
if (!ctx.IsTipc()) {
|
if (!ctx.IsTipc()) {
|
||||||
AlignWithPadding();
|
AlignWithPadding();
|
||||||
|
|
||||||
if (ctx.Session()->IsDomain() && ctx.HasDomainMessageHeader()) {
|
if (ctx.Session()->GetSessionRequestManager()->IsDomain() &&
|
||||||
|
ctx.HasDomainMessageHeader()) {
|
||||||
IPC::DomainMessageHeader domain_header{};
|
IPC::DomainMessageHeader domain_header{};
|
||||||
domain_header.num_objects = num_domain_objects;
|
domain_header.num_objects = num_domain_objects;
|
||||||
PushRaw(domain_header);
|
PushRaw(domain_header);
|
||||||
|
@ -145,7 +146,7 @@ public:
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void PushIpcInterface(std::shared_ptr<T> iface) {
|
void PushIpcInterface(std::shared_ptr<T> iface) {
|
||||||
if (context->Session()->IsDomain()) {
|
if (context->Session()->GetSessionRequestManager()->IsDomain()) {
|
||||||
context->AddDomainObject(std::move(iface));
|
context->AddDomainObject(std::move(iface));
|
||||||
} else {
|
} else {
|
||||||
kernel.CurrentProcess()->GetResourceLimit()->Reserve(
|
kernel.CurrentProcess()->GetResourceLimit()->Reserve(
|
||||||
|
@ -386,7 +387,7 @@ public:
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
std::weak_ptr<T> PopIpcInterface() {
|
std::weak_ptr<T> PopIpcInterface() {
|
||||||
ASSERT(context->Session()->IsDomain());
|
ASSERT(context->Session()->GetSessionRequestManager()->IsDomain());
|
||||||
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
|
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
|
||||||
return context->GetDomainHandler<T>(Pop<u32>() - 1);
|
return context->GetDomainHandler<T>(Pop<u32>() - 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "core/hle/kernel/k_server_session.h"
|
#include "core/hle/kernel/k_server_session.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
#include "core/hle/kernel/service_thread.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
@ -56,16 +57,103 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result SessionRequestManager::CompleteSyncRequest(KServerSession* server_session,
|
||||||
|
HLERequestContext& context) {
|
||||||
|
Result result = ResultSuccess;
|
||||||
|
|
||||||
|
// If the session has been converted to a domain, handle the domain request
|
||||||
|
if (this->HasSessionRequestHandler(context)) {
|
||||||
|
if (IsDomain() && context.HasDomainMessageHeader()) {
|
||||||
|
result = HandleDomainSyncRequest(server_session, context);
|
||||||
|
// If there is no domain header, the regular session handler is used
|
||||||
|
} else if (this->HasSessionHandler()) {
|
||||||
|
// If this manager has an associated HLE handler, forward the request to it.
|
||||||
|
result = this->SessionHandler().HandleSyncRequest(*server_session, context);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(false, "Session handler is invalid, stubbing response!");
|
||||||
|
IPC::ResponseBuilder rb(context, 2);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (convert_to_domain) {
|
||||||
|
ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance.");
|
||||||
|
this->ConvertToDomain();
|
||||||
|
convert_to_domain = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_session,
|
||||||
|
HLERequestContext& context) {
|
||||||
|
if (!context.HasDomainMessageHeader()) {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
|
||||||
|
context.SetSessionRequestManager(server_session->GetSessionRequestManager());
|
||||||
|
|
||||||
|
// If there is a DomainMessageHeader, then this is CommandType "Request"
|
||||||
|
const auto& domain_message_header = context.GetDomainMessageHeader();
|
||||||
|
const u32 object_id{domain_message_header.object_id};
|
||||||
|
switch (domain_message_header.command) {
|
||||||
|
case IPC::DomainMessageHeader::CommandType::SendMessage:
|
||||||
|
if (object_id > this->DomainHandlerCount()) {
|
||||||
|
LOG_CRITICAL(IPC,
|
||||||
|
"object_id {} is too big! This probably means a recent service call "
|
||||||
|
"needed to return a new interface!",
|
||||||
|
object_id);
|
||||||
|
ASSERT(false);
|
||||||
|
return ResultSuccess; // Ignore error if asserts are off
|
||||||
|
}
|
||||||
|
if (auto strong_ptr = this->DomainHandler(object_id - 1).lock()) {
|
||||||
|
return strong_ptr->HandleSyncRequest(*server_session, context);
|
||||||
|
} else {
|
||||||
|
ASSERT(false);
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
|
||||||
|
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
|
||||||
|
|
||||||
|
this->CloseDomainHandler(object_id - 1);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{context, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value());
|
||||||
|
ASSERT(false);
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SessionRequestManager::QueueSyncRequest(KSession* parent,
|
||||||
|
std::shared_ptr<HLERequestContext>&& context) {
|
||||||
|
// Ensure we have a session request handler
|
||||||
|
if (this->HasSessionRequestHandler(*context)) {
|
||||||
|
if (auto strong_ptr = this->GetServiceThread().lock()) {
|
||||||
|
strong_ptr->QueueSyncRequest(*parent, std::move(context));
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(false, "strong_ptr is nullptr!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(false, "handler is invalid!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
void SessionRequestHandler::ClientConnected(KServerSession* session) {
|
void SessionRequestHandler::ClientConnected(KServerSession* session) {
|
||||||
session->ClientConnected(shared_from_this());
|
session->GetSessionRequestManager()->SetSessionHandler(shared_from_this());
|
||||||
|
|
||||||
// Ensure our server session is tracked globally.
|
// Ensure our server session is tracked globally.
|
||||||
kernel.RegisterServerObject(session);
|
kernel.RegisterServerObject(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
|
void SessionRequestHandler::ClientDisconnected(KServerSession* session) {}
|
||||||
session->ClientDisconnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
|
HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
|
||||||
KServerSession* server_session_, KThread* thread_)
|
KServerSession* server_session_, KThread* thread_)
|
||||||
|
@ -126,7 +214,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
|
||||||
// Padding to align to 16 bytes
|
// Padding to align to 16 bytes
|
||||||
rp.AlignWithPadding();
|
rp.AlignWithPadding();
|
||||||
|
|
||||||
if (Session()->IsDomain() &&
|
if (Session()->GetSessionRequestManager()->IsDomain() &&
|
||||||
((command_header->type == IPC::CommandType::Request ||
|
((command_header->type == IPC::CommandType::Request ||
|
||||||
command_header->type == IPC::CommandType::RequestWithContext) ||
|
command_header->type == IPC::CommandType::RequestWithContext) ||
|
||||||
!incoming)) {
|
!incoming)) {
|
||||||
|
@ -135,7 +223,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
|
||||||
if (incoming || domain_message_header) {
|
if (incoming || domain_message_header) {
|
||||||
domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
|
domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
|
||||||
} else {
|
} else {
|
||||||
if (Session()->IsDomain()) {
|
if (Session()->GetSessionRequestManager()->IsDomain()) {
|
||||||
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
|
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,12 +316,12 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
|
||||||
// Write the domain objects to the command buffer, these go after the raw untranslated data.
|
// Write the domain objects to the command buffer, these go after the raw untranslated data.
|
||||||
// TODO(Subv): This completely ignores C buffers.
|
// TODO(Subv): This completely ignores C buffers.
|
||||||
|
|
||||||
if (Session()->IsDomain()) {
|
if (server_session->GetSessionRequestManager()->IsDomain()) {
|
||||||
current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
|
current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
|
||||||
for (const auto& object : outgoing_domain_objects) {
|
for (auto& object : outgoing_domain_objects) {
|
||||||
server_session->AppendDomainHandler(object);
|
server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object));
|
||||||
cmd_buf[current_offset++] =
|
cmd_buf[current_offset++] = static_cast<u32_le>(
|
||||||
static_cast<u32_le>(server_session->NumDomainRequestHandlers());
|
server_session->GetSessionRequestManager()->DomainHandlerCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,10 @@ public:
|
||||||
is_domain = true;
|
is_domain = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConvertToDomainOnRequestEnd() {
|
||||||
|
convert_to_domain = true;
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t DomainHandlerCount() const {
|
std::size_t DomainHandlerCount() const {
|
||||||
return domain_handlers.size();
|
return domain_handlers.size();
|
||||||
}
|
}
|
||||||
|
@ -164,7 +168,12 @@ public:
|
||||||
|
|
||||||
bool HasSessionRequestHandler(const HLERequestContext& context) const;
|
bool HasSessionRequestHandler(const HLERequestContext& context) const;
|
||||||
|
|
||||||
|
Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
|
||||||
|
Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
|
||||||
|
Result QueueSyncRequest(KSession* parent, std::shared_ptr<HLERequestContext>&& context);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool convert_to_domain{};
|
||||||
bool is_domain{};
|
bool is_domain{};
|
||||||
SessionRequestHandlerPtr session_handler;
|
SessionRequestHandlerPtr session_handler;
|
||||||
std::vector<SessionRequestHandlerPtr> domain_handlers;
|
std::vector<SessionRequestHandlerPtr> domain_handlers;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/k_resource_limit.h"
|
#include "core/hle/kernel/k_resource_limit.h"
|
||||||
#include "core/hle/kernel/k_session.h"
|
#include "core/hle/kernel/k_session.h"
|
||||||
|
#include "core/hle/kernel/k_session_request.h"
|
||||||
#include "core/hle/kernel/k_shared_memory.h"
|
#include "core/hle/kernel/k_shared_memory.h"
|
||||||
#include "core/hle/kernel/k_shared_memory_info.h"
|
#include "core/hle/kernel/k_shared_memory_info.h"
|
||||||
#include "core/hle/kernel/k_system_control.h"
|
#include "core/hle/kernel/k_system_control.h"
|
||||||
|
@ -34,6 +35,7 @@ namespace Kernel::Init {
|
||||||
HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \
|
HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \
|
||||||
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
|
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
|
||||||
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
|
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
|
||||||
|
HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ##__VA_ARGS__) \
|
||||||
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
|
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
|
||||||
HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
|
HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
|
||||||
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
|
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/scope_exit.h"
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
#include "core/hle/kernel/k_client_session.h"
|
#include "core/hle/kernel/k_client_session.h"
|
||||||
#include "core/hle/kernel/k_server_session.h"
|
#include "core/hle/kernel/k_server_session.h"
|
||||||
|
@ -10,6 +11,8 @@
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
static constexpr u32 MessageBufferSize = 0x100;
|
||||||
|
|
||||||
KClientSession::KClientSession(KernelCore& kernel_)
|
KClientSession::KClientSession(KernelCore& kernel_)
|
||||||
: KAutoObjectWithSlabHeapAndContainer{kernel_} {}
|
: KAutoObjectWithSlabHeapAndContainer{kernel_} {}
|
||||||
KClientSession::~KClientSession() = default;
|
KClientSession::~KClientSession() = default;
|
||||||
|
@ -22,8 +25,16 @@ void KClientSession::Destroy() {
|
||||||
void KClientSession::OnServerClosed() {}
|
void KClientSession::OnServerClosed() {}
|
||||||
|
|
||||||
Result KClientSession::SendSyncRequest() {
|
Result KClientSession::SendSyncRequest() {
|
||||||
// Signal the server session that new data is available
|
// Create a session request.
|
||||||
return parent->GetServerSession().OnRequest();
|
KSessionRequest* request = KSessionRequest::Create(kernel);
|
||||||
|
R_UNLESS(request != nullptr, ResultOutOfResource);
|
||||||
|
SCOPE_EXIT({ request->Close(); });
|
||||||
|
|
||||||
|
// Initialize the request.
|
||||||
|
request->Initialize(nullptr, GetCurrentThread(kernel).GetTLSAddress(), MessageBufferSize);
|
||||||
|
|
||||||
|
// Send the request.
|
||||||
|
return parent->GetServerSession().OnRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -16,6 +16,7 @@ class KLinkedListNode : public boost::intrusive::list_base_hook<>,
|
||||||
public KSlabAllocated<KLinkedListNode> {
|
public KSlabAllocated<KLinkedListNode> {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
explicit KLinkedListNode(KernelCore&) {}
|
||||||
KLinkedListNode() = default;
|
KLinkedListNode() = default;
|
||||||
|
|
||||||
void Initialize(void* it) {
|
void Initialize(void* it) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace Kernel {
|
||||||
|
|
||||||
class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
|
class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
|
||||||
public:
|
public:
|
||||||
|
explicit KPageBuffer(KernelCore&) {}
|
||||||
KPageBuffer() = default;
|
KPageBuffer() = default;
|
||||||
|
|
||||||
static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
|
static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
|
||||||
|
|
|
@ -22,15 +22,12 @@
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/kernel/k_thread_queue.h"
|
#include "core/hle/kernel/k_thread_queue.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/service_thread.h"
|
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
|
using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
|
||||||
|
|
||||||
static constexpr u32 MessageBufferSize = 0x100;
|
|
||||||
|
|
||||||
KServerSession::KServerSession(KernelCore& kernel_)
|
KServerSession::KServerSession(KernelCore& kernel_)
|
||||||
: KSynchronizationObject{kernel_}, m_lock{kernel_} {}
|
: KSynchronizationObject{kernel_}, m_lock{kernel_} {}
|
||||||
|
|
||||||
|
@ -73,59 +70,7 @@ bool KServerSession::IsSignaled() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we're signaled if we have a request and aren't handling one.
|
// Otherwise, we're signaled if we have a request and aren't handling one.
|
||||||
return !m_thread_request_list.empty() && m_current_thread_request == nullptr;
|
return !m_request_list.empty() && m_current_request == nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) {
|
|
||||||
manager->AppendDomainHandler(std::move(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t KServerSession::NumDomainRequestHandlers() const {
|
|
||||||
return manager->DomainHandlerCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
|
|
||||||
if (!context.HasDomainMessageHeader()) {
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
|
|
||||||
context.SetSessionRequestManager(manager);
|
|
||||||
|
|
||||||
// If there is a DomainMessageHeader, then this is CommandType "Request"
|
|
||||||
const auto& domain_message_header = context.GetDomainMessageHeader();
|
|
||||||
const u32 object_id{domain_message_header.object_id};
|
|
||||||
switch (domain_message_header.command) {
|
|
||||||
case IPC::DomainMessageHeader::CommandType::SendMessage:
|
|
||||||
if (object_id > manager->DomainHandlerCount()) {
|
|
||||||
LOG_CRITICAL(IPC,
|
|
||||||
"object_id {} is too big! This probably means a recent service call "
|
|
||||||
"to {} needed to return a new interface!",
|
|
||||||
object_id, name);
|
|
||||||
ASSERT(false);
|
|
||||||
return ResultSuccess; // Ignore error if asserts are off
|
|
||||||
}
|
|
||||||
if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
|
|
||||||
return strong_ptr->HandleSyncRequest(*this, context);
|
|
||||||
} else {
|
|
||||||
ASSERT(false);
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
|
|
||||||
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
|
|
||||||
|
|
||||||
manager->CloseDomainHandler(object_id - 1);
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{context, 2};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value());
|
|
||||||
ASSERT(false);
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
|
Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
|
||||||
|
@ -134,43 +79,11 @@ Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& m
|
||||||
|
|
||||||
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||||
|
|
||||||
// Ensure we have a session request handler
|
return manager->QueueSyncRequest(parent, std::move(context));
|
||||||
if (manager->HasSessionRequestHandler(*context)) {
|
|
||||||
if (auto strong_ptr = manager->GetServiceThread().lock()) {
|
|
||||||
strong_ptr->QueueSyncRequest(*parent, std::move(context));
|
|
||||||
} else {
|
|
||||||
ASSERT_MSG(false, "strong_ptr is nullptr!");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ASSERT_MSG(false, "handler is invalid!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||||
Result result = ResultSuccess;
|
Result result = manager->CompleteSyncRequest(this, context);
|
||||||
|
|
||||||
// If the session has been converted to a domain, handle the domain request
|
|
||||||
if (manager->HasSessionRequestHandler(context)) {
|
|
||||||
if (IsDomain() && context.HasDomainMessageHeader()) {
|
|
||||||
result = HandleDomainSyncRequest(context);
|
|
||||||
// If there is no domain header, the regular session handler is used
|
|
||||||
} else if (manager->HasSessionHandler()) {
|
|
||||||
// If this ServerSession has an associated HLE handler, forward the request to it.
|
|
||||||
result = manager->SessionHandler().HandleSyncRequest(*this, context);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ASSERT_MSG(false, "Session handler is invalid, stubbing response!");
|
|
||||||
IPC::ResponseBuilder rb(context, 2);
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (convert_to_domain) {
|
|
||||||
ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance.");
|
|
||||||
manager->ConvertToDomain();
|
|
||||||
convert_to_domain = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The calling thread is waiting for this request to complete, so wake it up.
|
// The calling thread is waiting for this request to complete, so wake it up.
|
||||||
context.GetThread().EndWait(result);
|
context.GetThread().EndWait(result);
|
||||||
|
@ -178,7 +91,7 @@ Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KServerSession::OnRequest() {
|
Result KServerSession::OnRequest(KSessionRequest* request) {
|
||||||
// Create the wait queue.
|
// Create the wait queue.
|
||||||
ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
|
ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
|
||||||
|
|
||||||
|
@ -198,14 +111,13 @@ Result KServerSession::OnRequest() {
|
||||||
this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory);
|
this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory);
|
||||||
} else {
|
} else {
|
||||||
// Non-HLE request.
|
// Non-HLE request.
|
||||||
auto* thread{GetCurrentThreadPointer(kernel)};
|
|
||||||
|
|
||||||
// Get whether we're empty.
|
// Get whether we're empty.
|
||||||
const bool was_empty = m_thread_request_list.empty();
|
const bool was_empty = m_request_list.empty();
|
||||||
|
|
||||||
// Add the thread to the list.
|
// Add the request to the list.
|
||||||
thread->Open();
|
request->Open();
|
||||||
m_thread_request_list.push_back(thread);
|
m_request_list.push_back(*request);
|
||||||
|
|
||||||
// If we were empty, signal.
|
// If we were empty, signal.
|
||||||
if (was_empty) {
|
if (was_empty) {
|
||||||
|
@ -213,6 +125,9 @@ Result KServerSession::OnRequest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have a request event, this is asynchronous, and we don't need to wait.
|
||||||
|
R_SUCCEED_IF(request->GetEvent() != nullptr);
|
||||||
|
|
||||||
// This is a synchronous request, so we should wait for our request to complete.
|
// This is a synchronous request, so we should wait for our request to complete.
|
||||||
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
||||||
GetCurrentThread(kernel).BeginWait(&wait_queue);
|
GetCurrentThread(kernel).BeginWait(&wait_queue);
|
||||||
|
@ -223,32 +138,32 @@ Result KServerSession::OnRequest() {
|
||||||
|
|
||||||
Result KServerSession::SendReply() {
|
Result KServerSession::SendReply() {
|
||||||
// Lock the session.
|
// Lock the session.
|
||||||
KScopedLightLock lk(m_lock);
|
KScopedLightLock lk{m_lock};
|
||||||
|
|
||||||
// Get the request.
|
// Get the request.
|
||||||
KThread* client_thread;
|
KSessionRequest* request;
|
||||||
{
|
{
|
||||||
KScopedSchedulerLock sl{kernel};
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
// Get the current request.
|
// Get the current request.
|
||||||
client_thread = m_current_thread_request;
|
request = m_current_request;
|
||||||
R_UNLESS(client_thread != nullptr, ResultInvalidState);
|
R_UNLESS(request != nullptr, ResultInvalidState);
|
||||||
|
|
||||||
// Clear the current request, since we're processing it.
|
// Clear the current request, since we're processing it.
|
||||||
m_current_thread_request = nullptr;
|
m_current_request = nullptr;
|
||||||
if (!m_thread_request_list.empty()) {
|
if (!m_request_list.empty()) {
|
||||||
this->NotifyAvailable();
|
this->NotifyAvailable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close reference to the request once we're done processing it.
|
// Close reference to the request once we're done processing it.
|
||||||
SCOPE_EXIT({ client_thread->Close(); });
|
SCOPE_EXIT({ request->Close(); });
|
||||||
|
|
||||||
// Extract relevant information from the request.
|
// Extract relevant information from the request.
|
||||||
// const uintptr_t client_message = request->GetAddress();
|
const uintptr_t client_message = request->GetAddress();
|
||||||
// const size_t client_buffer_size = request->GetSize();
|
const size_t client_buffer_size = request->GetSize();
|
||||||
// KThread *client_thread = request->GetThread();
|
KThread* client_thread = request->GetThread();
|
||||||
// KEvent *event = request->GetEvent();
|
KEvent* event = request->GetEvent();
|
||||||
|
|
||||||
// Check whether we're closed.
|
// Check whether we're closed.
|
||||||
const bool closed = (client_thread == nullptr || parent->IsClientClosed());
|
const bool closed = (client_thread == nullptr || parent->IsClientClosed());
|
||||||
|
@ -261,8 +176,8 @@ Result KServerSession::SendReply() {
|
||||||
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
|
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
|
||||||
|
|
||||||
auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
|
auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
|
||||||
auto* dst_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress());
|
auto* dst_msg_buffer = memory.GetPointer(client_message);
|
||||||
std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize);
|
std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
|
||||||
} else {
|
} else {
|
||||||
result = ResultSessionClosed;
|
result = ResultSessionClosed;
|
||||||
}
|
}
|
||||||
|
@ -278,11 +193,30 @@ Result KServerSession::SendReply() {
|
||||||
|
|
||||||
// If there's a client thread, update it.
|
// If there's a client thread, update it.
|
||||||
if (client_thread != nullptr) {
|
if (client_thread != nullptr) {
|
||||||
// End the client thread's wait.
|
if (event != nullptr) {
|
||||||
KScopedSchedulerLock sl{kernel};
|
// // Get the client process/page table.
|
||||||
|
// KProcess *client_process = client_thread->GetOwnerProcess();
|
||||||
|
// KPageTable *client_page_table = &client_process->PageTable();
|
||||||
|
|
||||||
if (!client_thread->IsTerminationRequested()) {
|
// // If we need to, reply with an async error.
|
||||||
client_thread->EndWait(client_result);
|
// if (R_FAILED(client_result)) {
|
||||||
|
// ReplyAsyncError(client_process, client_message, client_buffer_size,
|
||||||
|
// client_result);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Unlock the client buffer.
|
||||||
|
// // NOTE: Nintendo does not check the result of this.
|
||||||
|
// client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
|
||||||
|
|
||||||
|
// Signal the event.
|
||||||
|
event->Signal();
|
||||||
|
} else {
|
||||||
|
// End the client thread's wait.
|
||||||
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
|
if (!client_thread->IsTerminationRequested()) {
|
||||||
|
client_thread->EndWait(client_result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,10 +225,10 @@ Result KServerSession::SendReply() {
|
||||||
|
|
||||||
Result KServerSession::ReceiveRequest() {
|
Result KServerSession::ReceiveRequest() {
|
||||||
// Lock the session.
|
// Lock the session.
|
||||||
KScopedLightLock lk(m_lock);
|
KScopedLightLock lk{m_lock};
|
||||||
|
|
||||||
// Get the request and client thread.
|
// Get the request and client thread.
|
||||||
// KSessionRequest *request;
|
KSessionRequest* request;
|
||||||
KThread* client_thread;
|
KThread* client_thread;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -304,35 +238,41 @@ Result KServerSession::ReceiveRequest() {
|
||||||
R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
|
R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
|
||||||
|
|
||||||
// Ensure we aren't already servicing a request.
|
// Ensure we aren't already servicing a request.
|
||||||
R_UNLESS(m_current_thread_request == nullptr, ResultNotFound);
|
R_UNLESS(m_current_request == nullptr, ResultNotFound);
|
||||||
|
|
||||||
// Ensure we have a request to service.
|
// Ensure we have a request to service.
|
||||||
R_UNLESS(!m_thread_request_list.empty(), ResultNotFound);
|
R_UNLESS(!m_request_list.empty(), ResultNotFound);
|
||||||
|
|
||||||
// Pop the first request from the list.
|
// Pop the first request from the list.
|
||||||
client_thread = m_thread_request_list.front();
|
request = &m_request_list.front();
|
||||||
m_thread_request_list.pop_front();
|
m_request_list.pop_front();
|
||||||
|
|
||||||
// Get the thread for the request.
|
// Get the thread for the request.
|
||||||
|
client_thread = request->GetThread();
|
||||||
R_UNLESS(client_thread != nullptr, ResultSessionClosed);
|
R_UNLESS(client_thread != nullptr, ResultSessionClosed);
|
||||||
|
|
||||||
// Open the client thread.
|
// Open the client thread.
|
||||||
client_thread->Open();
|
client_thread->Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
// SCOPE_EXIT({ client_thread->Close(); });
|
SCOPE_EXIT({ client_thread->Close(); });
|
||||||
|
|
||||||
// Set the request as our current.
|
// Set the request as our current.
|
||||||
m_current_thread_request = client_thread;
|
m_current_request = request;
|
||||||
|
|
||||||
|
// Get the client address.
|
||||||
|
uintptr_t client_message = request->GetAddress();
|
||||||
|
size_t client_buffer_size = request->GetSize();
|
||||||
|
// bool recv_list_broken = false;
|
||||||
|
|
||||||
// Receive the message.
|
// Receive the message.
|
||||||
Core::Memory::Memory& memory{kernel.System().Memory()};
|
Core::Memory::Memory& memory{kernel.System().Memory()};
|
||||||
KThread* server_thread{GetCurrentThreadPointer(kernel)};
|
KThread* server_thread{GetCurrentThreadPointer(kernel)};
|
||||||
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
|
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
|
||||||
|
|
||||||
auto* src_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress());
|
auto* src_msg_buffer = memory.GetPointer(client_message);
|
||||||
auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
|
auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
|
||||||
std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize);
|
std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
|
||||||
|
|
||||||
// We succeeded.
|
// We succeeded.
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
|
@ -344,35 +284,34 @@ void KServerSession::CleanupRequests() {
|
||||||
// Clean up any pending requests.
|
// Clean up any pending requests.
|
||||||
while (true) {
|
while (true) {
|
||||||
// Get the next request.
|
// Get the next request.
|
||||||
// KSessionRequest *request = nullptr;
|
KSessionRequest* request = nullptr;
|
||||||
KThread* client_thread = nullptr;
|
|
||||||
{
|
{
|
||||||
KScopedSchedulerLock sl{kernel};
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
if (m_current_thread_request) {
|
if (m_current_request) {
|
||||||
// Choose the current request if we have one.
|
// Choose the current request if we have one.
|
||||||
client_thread = m_current_thread_request;
|
request = m_current_request;
|
||||||
m_current_thread_request = nullptr;
|
m_current_request = nullptr;
|
||||||
} else if (!m_thread_request_list.empty()) {
|
} else if (!m_request_list.empty()) {
|
||||||
// Pop the request from the front of the list.
|
// Pop the request from the front of the list.
|
||||||
client_thread = m_thread_request_list.front();
|
request = &m_request_list.front();
|
||||||
m_thread_request_list.pop_front();
|
m_request_list.pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's no request, we're done.
|
// If there's no request, we're done.
|
||||||
if (client_thread == nullptr) {
|
if (request == nullptr) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close a reference to the request once it's cleaned up.
|
// Close a reference to the request once it's cleaned up.
|
||||||
SCOPE_EXIT({ client_thread->Close(); });
|
SCOPE_EXIT({ request->Close(); });
|
||||||
|
|
||||||
// Extract relevant information from the request.
|
// Extract relevant information from the request.
|
||||||
// const uintptr_t client_message = request->GetAddress();
|
// const uintptr_t client_message = request->GetAddress();
|
||||||
// const size_t client_buffer_size = request->GetSize();
|
// const size_t client_buffer_size = request->GetSize();
|
||||||
// KThread *client_thread = request->GetThread();
|
KThread* client_thread = request->GetThread();
|
||||||
// KEvent *event = request->GetEvent();
|
KEvent* event = request->GetEvent();
|
||||||
|
|
||||||
// KProcess *server_process = request->GetServerProcess();
|
// KProcess *server_process = request->GetServerProcess();
|
||||||
// KProcess *client_process = (client_thread != nullptr) ?
|
// KProcess *client_process = (client_thread != nullptr) ?
|
||||||
|
@ -385,11 +324,24 @@ void KServerSession::CleanupRequests() {
|
||||||
|
|
||||||
// If there's a client thread, update it.
|
// If there's a client thread, update it.
|
||||||
if (client_thread != nullptr) {
|
if (client_thread != nullptr) {
|
||||||
// End the client thread's wait.
|
if (event != nullptr) {
|
||||||
KScopedSchedulerLock sl{kernel};
|
// // We need to reply async.
|
||||||
|
// ReplyAsyncError(client_process, client_message, client_buffer_size,
|
||||||
|
// (R_SUCCEEDED(result) ? ResultSessionClosed : result));
|
||||||
|
|
||||||
if (!client_thread->IsTerminationRequested()) {
|
// // Unlock the client buffer.
|
||||||
client_thread->EndWait(ResultSessionClosed);
|
// NOTE: Nintendo does not check the result of this.
|
||||||
|
// client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
|
||||||
|
|
||||||
|
// Signal the event.
|
||||||
|
event->Signal();
|
||||||
|
} else {
|
||||||
|
// End the client thread's wait.
|
||||||
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
|
if (!client_thread->IsTerminationRequested()) {
|
||||||
|
client_thread->EndWait(ResultSessionClosed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
#include "core/hle/kernel/k_light_lock.h"
|
#include "core/hle/kernel/k_light_lock.h"
|
||||||
|
#include "core/hle/kernel/k_session_request.h"
|
||||||
#include "core/hle/kernel/k_synchronization_object.h"
|
#include "core/hle/kernel/k_synchronization_object.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
|
@ -57,44 +58,15 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSignaled() const override;
|
bool IsSignaled() const override;
|
||||||
|
|
||||||
void OnClientClosed();
|
void OnClientClosed();
|
||||||
|
|
||||||
void ClientConnected(SessionRequestHandlerPtr handler) {
|
|
||||||
if (manager) {
|
|
||||||
manager->SetSessionHandler(std::move(handler));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDisconnected() {
|
|
||||||
manager = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a new domain request handler to the collection of request handlers within
|
|
||||||
/// this ServerSession instance.
|
|
||||||
void AppendDomainHandler(SessionRequestHandlerPtr handler);
|
|
||||||
|
|
||||||
/// Retrieves the total number of domain request handlers that have been
|
|
||||||
/// appended to this ServerSession instance.
|
|
||||||
std::size_t NumDomainRequestHandlers() const;
|
|
||||||
|
|
||||||
/// Returns true if the session has been converted to a domain, otherwise False
|
|
||||||
bool IsDomain() const {
|
|
||||||
return manager && manager->IsDomain();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts the session to a domain at the end of the current command
|
|
||||||
void ConvertToDomain() {
|
|
||||||
convert_to_domain = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the session request manager, which forwards requests to the underlying service
|
/// Gets the session request manager, which forwards requests to the underlying service
|
||||||
std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() {
|
std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() {
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: flesh these out to match the real kernel
|
/// TODO: flesh these out to match the real kernel
|
||||||
Result OnRequest();
|
Result OnRequest(KSessionRequest* request);
|
||||||
Result SendReply();
|
Result SendReply();
|
||||||
Result ReceiveRequest();
|
Result ReceiveRequest();
|
||||||
|
|
||||||
|
@ -108,10 +80,6 @@ private:
|
||||||
/// Completes a sync request from the emulated application.
|
/// Completes a sync request from the emulated application.
|
||||||
Result CompleteSyncRequest(HLERequestContext& context);
|
Result CompleteSyncRequest(HLERequestContext& context);
|
||||||
|
|
||||||
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
|
|
||||||
/// object handle.
|
|
||||||
Result HandleDomainSyncRequest(Kernel::HLERequestContext& context);
|
|
||||||
|
|
||||||
/// This session's HLE request handlers; if nullptr, this is not an HLE server
|
/// This session's HLE request handlers; if nullptr, this is not an HLE server
|
||||||
std::shared_ptr<SessionRequestManager> manager;
|
std::shared_ptr<SessionRequestManager> manager;
|
||||||
|
|
||||||
|
@ -122,9 +90,8 @@ private:
|
||||||
KSession* parent{};
|
KSession* parent{};
|
||||||
|
|
||||||
/// List of threads which are pending a reply.
|
/// List of threads which are pending a reply.
|
||||||
/// FIXME: KSessionRequest
|
boost::intrusive::list<KSessionRequest> m_request_list;
|
||||||
std::list<KThread*> m_thread_request_list;
|
KSessionRequest* m_current_request;
|
||||||
KThread* m_current_thread_request{};
|
|
||||||
|
|
||||||
KLightLock m_lock;
|
KLightLock m_lock;
|
||||||
};
|
};
|
||||||
|
|
61
src/core/hle/kernel/k_session_request.cpp
Executable file
61
src/core/hle/kernel/k_session_request.cpp
Executable file
|
@ -0,0 +1,61 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/kernel/k_page_buffer.h"
|
||||||
|
#include "core/hle/kernel/k_session_request.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
Result KSessionRequest::SessionMappings::PushMap(VAddr client, VAddr server, size_t size,
|
||||||
|
KMemoryState state, size_t index) {
|
||||||
|
// At most 15 buffers of each type (4-bit descriptor counts).
|
||||||
|
ASSERT(index < ((1ul << 4) - 1) * 3);
|
||||||
|
|
||||||
|
// Get the mapping.
|
||||||
|
Mapping* mapping;
|
||||||
|
if (index < NumStaticMappings) {
|
||||||
|
mapping = &m_static_mappings[index];
|
||||||
|
} else {
|
||||||
|
// Allocate a page for the extra mappings.
|
||||||
|
if (m_mappings == nullptr) {
|
||||||
|
KPageBuffer* page_buffer = KPageBuffer::Allocate(kernel);
|
||||||
|
R_UNLESS(page_buffer != nullptr, ResultOutOfMemory);
|
||||||
|
|
||||||
|
m_mappings = reinterpret_cast<Mapping*>(page_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping = &m_mappings[index - NumStaticMappings];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the mapping.
|
||||||
|
mapping->Set(client, server, size, state);
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KSessionRequest::SessionMappings::PushSend(VAddr client, VAddr server, size_t size,
|
||||||
|
KMemoryState state) {
|
||||||
|
ASSERT(m_num_recv == 0);
|
||||||
|
ASSERT(m_num_exch == 0);
|
||||||
|
return this->PushMap(client, server, size, state, m_num_send++);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KSessionRequest::SessionMappings::PushReceive(VAddr client, VAddr server, size_t size,
|
||||||
|
KMemoryState state) {
|
||||||
|
ASSERT(m_num_exch == 0);
|
||||||
|
return this->PushMap(client, server, size, state, m_num_send + m_num_recv++);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KSessionRequest::SessionMappings::PushExchange(VAddr client, VAddr server, size_t size,
|
||||||
|
KMemoryState state) {
|
||||||
|
return this->PushMap(client, server, size, state, m_num_send + m_num_recv + m_num_exch++);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSessionRequest::SessionMappings::Finalize() {
|
||||||
|
if (m_mappings) {
|
||||||
|
KPageBuffer::Free(kernel, reinterpret_cast<KPageBuffer*>(m_mappings));
|
||||||
|
m_mappings = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Kernel
|
307
src/core/hle/kernel/k_session_request.h
Executable file
307
src/core/hle/kernel/k_session_request.h
Executable file
|
@ -0,0 +1,307 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/hle/kernel/k_auto_object.h"
|
||||||
|
#include "core/hle/kernel/k_event.h"
|
||||||
|
#include "core/hle/kernel/k_memory_block.h"
|
||||||
|
#include "core/hle/kernel/k_process.h"
|
||||||
|
#include "core/hle/kernel/k_thread.h"
|
||||||
|
#include "core/hle/kernel/slab_helpers.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class KSessionRequest final : public KSlabAllocated<KSessionRequest>,
|
||||||
|
public KAutoObject,
|
||||||
|
public boost::intrusive::list_base_hook<> {
|
||||||
|
KERNEL_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject);
|
||||||
|
|
||||||
|
public:
|
||||||
|
class SessionMappings {
|
||||||
|
private:
|
||||||
|
static constexpr size_t NumStaticMappings = 8;
|
||||||
|
|
||||||
|
class Mapping {
|
||||||
|
public:
|
||||||
|
constexpr void Set(VAddr c, VAddr s, size_t sz, KMemoryState st) {
|
||||||
|
m_client_address = c;
|
||||||
|
m_server_address = s;
|
||||||
|
m_size = sz;
|
||||||
|
m_state = st;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr VAddr GetClientAddress() const {
|
||||||
|
return m_client_address;
|
||||||
|
}
|
||||||
|
constexpr VAddr GetServerAddress() const {
|
||||||
|
return m_server_address;
|
||||||
|
}
|
||||||
|
constexpr size_t GetSize() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
constexpr KMemoryState GetMemoryState() const {
|
||||||
|
return m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
VAddr m_client_address;
|
||||||
|
VAddr m_server_address;
|
||||||
|
size_t m_size;
|
||||||
|
KMemoryState m_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SessionMappings(KernelCore& kernel_)
|
||||||
|
: kernel(kernel_), m_mappings(nullptr), m_num_send(), m_num_recv(), m_num_exch() {}
|
||||||
|
|
||||||
|
void Initialize() {}
|
||||||
|
void Finalize();
|
||||||
|
|
||||||
|
size_t GetSendCount() const {
|
||||||
|
return m_num_send;
|
||||||
|
}
|
||||||
|
size_t GetReceiveCount() const {
|
||||||
|
return m_num_recv;
|
||||||
|
}
|
||||||
|
size_t GetExchangeCount() const {
|
||||||
|
return m_num_exch;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state);
|
||||||
|
Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state);
|
||||||
|
Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state);
|
||||||
|
|
||||||
|
VAddr GetSendClientAddress(size_t i) const {
|
||||||
|
return GetSendMapping(i).GetClientAddress();
|
||||||
|
}
|
||||||
|
VAddr GetSendServerAddress(size_t i) const {
|
||||||
|
return GetSendMapping(i).GetServerAddress();
|
||||||
|
}
|
||||||
|
size_t GetSendSize(size_t i) const {
|
||||||
|
return GetSendMapping(i).GetSize();
|
||||||
|
}
|
||||||
|
KMemoryState GetSendMemoryState(size_t i) const {
|
||||||
|
return GetSendMapping(i).GetMemoryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetReceiveClientAddress(size_t i) const {
|
||||||
|
return GetReceiveMapping(i).GetClientAddress();
|
||||||
|
}
|
||||||
|
VAddr GetReceiveServerAddress(size_t i) const {
|
||||||
|
return GetReceiveMapping(i).GetServerAddress();
|
||||||
|
}
|
||||||
|
size_t GetReceiveSize(size_t i) const {
|
||||||
|
return GetReceiveMapping(i).GetSize();
|
||||||
|
}
|
||||||
|
KMemoryState GetReceiveMemoryState(size_t i) const {
|
||||||
|
return GetReceiveMapping(i).GetMemoryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetExchangeClientAddress(size_t i) const {
|
||||||
|
return GetExchangeMapping(i).GetClientAddress();
|
||||||
|
}
|
||||||
|
VAddr GetExchangeServerAddress(size_t i) const {
|
||||||
|
return GetExchangeMapping(i).GetServerAddress();
|
||||||
|
}
|
||||||
|
size_t GetExchangeSize(size_t i) const {
|
||||||
|
return GetExchangeMapping(i).GetSize();
|
||||||
|
}
|
||||||
|
KMemoryState GetExchangeMemoryState(size_t i) const {
|
||||||
|
return GetExchangeMapping(i).GetMemoryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Result PushMap(VAddr client, VAddr server, size_t size, KMemoryState state, size_t index);
|
||||||
|
|
||||||
|
const Mapping& GetSendMapping(size_t i) const {
|
||||||
|
ASSERT(i < m_num_send);
|
||||||
|
|
||||||
|
const size_t index = i;
|
||||||
|
if (index < NumStaticMappings) {
|
||||||
|
return m_static_mappings[index];
|
||||||
|
} else {
|
||||||
|
return m_mappings[index - NumStaticMappings];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Mapping& GetReceiveMapping(size_t i) const {
|
||||||
|
ASSERT(i < m_num_recv);
|
||||||
|
|
||||||
|
const size_t index = m_num_send + i;
|
||||||
|
if (index < NumStaticMappings) {
|
||||||
|
return m_static_mappings[index];
|
||||||
|
} else {
|
||||||
|
return m_mappings[index - NumStaticMappings];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Mapping& GetExchangeMapping(size_t i) const {
|
||||||
|
ASSERT(i < m_num_exch);
|
||||||
|
|
||||||
|
const size_t index = m_num_send + m_num_recv + i;
|
||||||
|
if (index < NumStaticMappings) {
|
||||||
|
return m_static_mappings[index];
|
||||||
|
} else {
|
||||||
|
return m_mappings[index - NumStaticMappings];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
KernelCore& kernel;
|
||||||
|
Mapping m_static_mappings[NumStaticMappings];
|
||||||
|
Mapping* m_mappings;
|
||||||
|
u8 m_num_send;
|
||||||
|
u8 m_num_recv;
|
||||||
|
u8 m_num_exch;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit KSessionRequest(KernelCore& kernel_)
|
||||||
|
: KAutoObject(kernel_), m_mappings(kernel_), m_thread(nullptr), m_server(nullptr),
|
||||||
|
m_event(nullptr) {}
|
||||||
|
|
||||||
|
static KSessionRequest* Create(KernelCore& kernel) {
|
||||||
|
KSessionRequest* req = KSessionRequest::Allocate(kernel);
|
||||||
|
if (req != nullptr) [[likely]] {
|
||||||
|
KAutoObject::Create(req);
|
||||||
|
}
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Destroy() override {
|
||||||
|
this->Finalize();
|
||||||
|
KSessionRequest::Free(kernel, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(KEvent* event, uintptr_t address, size_t size) {
|
||||||
|
m_mappings.Initialize();
|
||||||
|
|
||||||
|
m_thread = GetCurrentThreadPointer(kernel);
|
||||||
|
m_event = event;
|
||||||
|
m_address = address;
|
||||||
|
m_size = size;
|
||||||
|
|
||||||
|
m_thread->Open();
|
||||||
|
if (m_event != nullptr) {
|
||||||
|
m_event->Open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PostDestroy(uintptr_t arg) {}
|
||||||
|
|
||||||
|
KThread* GetThread() const {
|
||||||
|
return m_thread;
|
||||||
|
}
|
||||||
|
KEvent* GetEvent() const {
|
||||||
|
return m_event;
|
||||||
|
}
|
||||||
|
uintptr_t GetAddress() const {
|
||||||
|
return m_address;
|
||||||
|
}
|
||||||
|
size_t GetSize() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
KProcess* GetServerProcess() const {
|
||||||
|
return m_server;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetServerProcess(KProcess* process) {
|
||||||
|
m_server = process;
|
||||||
|
m_server->Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearThread() {
|
||||||
|
m_thread = nullptr;
|
||||||
|
}
|
||||||
|
void ClearEvent() {
|
||||||
|
m_event = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetSendCount() const {
|
||||||
|
return m_mappings.GetSendCount();
|
||||||
|
}
|
||||||
|
size_t GetReceiveCount() const {
|
||||||
|
return m_mappings.GetReceiveCount();
|
||||||
|
}
|
||||||
|
size_t GetExchangeCount() const {
|
||||||
|
return m_mappings.GetExchangeCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state) {
|
||||||
|
return m_mappings.PushSend(client, server, size, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state) {
|
||||||
|
return m_mappings.PushReceive(client, server, size, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state) {
|
||||||
|
return m_mappings.PushExchange(client, server, size, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetSendClientAddress(size_t i) const {
|
||||||
|
return m_mappings.GetSendClientAddress(i);
|
||||||
|
}
|
||||||
|
VAddr GetSendServerAddress(size_t i) const {
|
||||||
|
return m_mappings.GetSendServerAddress(i);
|
||||||
|
}
|
||||||
|
size_t GetSendSize(size_t i) const {
|
||||||
|
return m_mappings.GetSendSize(i);
|
||||||
|
}
|
||||||
|
KMemoryState GetSendMemoryState(size_t i) const {
|
||||||
|
return m_mappings.GetSendMemoryState(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetReceiveClientAddress(size_t i) const {
|
||||||
|
return m_mappings.GetReceiveClientAddress(i);
|
||||||
|
}
|
||||||
|
VAddr GetReceiveServerAddress(size_t i) const {
|
||||||
|
return m_mappings.GetReceiveServerAddress(i);
|
||||||
|
}
|
||||||
|
size_t GetReceiveSize(size_t i) const {
|
||||||
|
return m_mappings.GetReceiveSize(i);
|
||||||
|
}
|
||||||
|
KMemoryState GetReceiveMemoryState(size_t i) const {
|
||||||
|
return m_mappings.GetReceiveMemoryState(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetExchangeClientAddress(size_t i) const {
|
||||||
|
return m_mappings.GetExchangeClientAddress(i);
|
||||||
|
}
|
||||||
|
VAddr GetExchangeServerAddress(size_t i) const {
|
||||||
|
return m_mappings.GetExchangeServerAddress(i);
|
||||||
|
}
|
||||||
|
size_t GetExchangeSize(size_t i) const {
|
||||||
|
return m_mappings.GetExchangeSize(i);
|
||||||
|
}
|
||||||
|
KMemoryState GetExchangeMemoryState(size_t i) const {
|
||||||
|
return m_mappings.GetExchangeMemoryState(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// NOTE: This is public and virtual in Nintendo's kernel.
|
||||||
|
void Finalize() {
|
||||||
|
m_mappings.Finalize();
|
||||||
|
|
||||||
|
if (m_thread) {
|
||||||
|
m_thread->Close();
|
||||||
|
}
|
||||||
|
if (m_event) {
|
||||||
|
m_event->Close();
|
||||||
|
}
|
||||||
|
if (m_server) {
|
||||||
|
m_server->Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SessionMappings m_mappings;
|
||||||
|
KThread* m_thread;
|
||||||
|
KProcess* m_server;
|
||||||
|
KEvent* m_event;
|
||||||
|
uintptr_t m_address;
|
||||||
|
size_t m_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Kernel
|
|
@ -15,7 +15,8 @@ class KSharedMemoryInfo final : public KSlabAllocated<KSharedMemoryInfo>,
|
||||||
public boost::intrusive::list_base_hook<> {
|
public boost::intrusive::list_base_hook<> {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit KSharedMemoryInfo() = default;
|
explicit KSharedMemoryInfo(KernelCore&) {}
|
||||||
|
KSharedMemoryInfo() = default;
|
||||||
|
|
||||||
constexpr void Initialize(KSharedMemory* shmem) {
|
constexpr void Initialize(KSharedMemory* shmem) {
|
||||||
shared_memory = shmem;
|
shared_memory = shmem;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/spin_lock.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
@ -36,28 +37,34 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void* Allocate() {
|
void* Allocate() {
|
||||||
Node* ret = m_head.load();
|
// KScopedInterruptDisable di;
|
||||||
|
|
||||||
do {
|
m_lock.lock();
|
||||||
if (ret == nullptr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (!m_head.compare_exchange_weak(ret, ret->next));
|
|
||||||
|
|
||||||
|
Node* ret = m_head;
|
||||||
|
if (ret != nullptr) [[likely]] {
|
||||||
|
m_head = ret->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lock.unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Free(void* obj) {
|
void Free(void* obj) {
|
||||||
Node* node = static_cast<Node*>(obj);
|
// KScopedInterruptDisable di;
|
||||||
|
|
||||||
Node* cur_head = m_head.load();
|
m_lock.lock();
|
||||||
do {
|
|
||||||
node->next = cur_head;
|
Node* node = static_cast<Node*>(obj);
|
||||||
} while (!m_head.compare_exchange_weak(cur_head, node));
|
node->next = m_head;
|
||||||
|
m_head = node;
|
||||||
|
|
||||||
|
m_lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<Node*> m_head{};
|
std::atomic<Node*> m_head{};
|
||||||
|
Common::SpinLock m_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace impl
|
} // namespace impl
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
static_assert(RegionsPerPage > 0);
|
static_assert(RegionsPerPage > 0);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) {
|
constexpr explicit KThreadLocalPage(KernelCore&, VAddr addr = {}) : m_virt_addr(addr) {
|
||||||
m_is_region_free.fill(true);
|
m_is_region_free.fill(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ class KResourceLimit;
|
||||||
class KScheduler;
|
class KScheduler;
|
||||||
class KServerSession;
|
class KServerSession;
|
||||||
class KSession;
|
class KSession;
|
||||||
|
class KSessionRequest;
|
||||||
class KSharedMemory;
|
class KSharedMemory;
|
||||||
class KSharedMemoryInfo;
|
class KSharedMemoryInfo;
|
||||||
class KThread;
|
class KThread;
|
||||||
|
@ -360,6 +361,8 @@ public:
|
||||||
return slab_heap_container->page_buffer;
|
return slab_heap_container->page_buffer;
|
||||||
} else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
|
} else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
|
||||||
return slab_heap_container->thread_local_page;
|
return slab_heap_container->thread_local_page;
|
||||||
|
} else if constexpr (std::is_same_v<T, KSessionRequest>) {
|
||||||
|
return slab_heap_container->session_request;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,6 +425,7 @@ private:
|
||||||
KSlabHeap<KCodeMemory> code_memory;
|
KSlabHeap<KCodeMemory> code_memory;
|
||||||
KSlabHeap<KPageBuffer> page_buffer;
|
KSlabHeap<KPageBuffer> page_buffer;
|
||||||
KSlabHeap<KThreadLocalPage> thread_local_page;
|
KSlabHeap<KThreadLocalPage> thread_local_page;
|
||||||
|
KSlabHeap<KSessionRequest> session_request;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<SlabHeapContainer> slab_heap_container;
|
std::unique_ptr<SlabHeapContainer> slab_heap_container;
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
static Derived* Allocate(KernelCore& kernel) {
|
static Derived* Allocate(KernelCore& kernel) {
|
||||||
return kernel.SlabHeap<Derived>().Allocate();
|
return kernel.SlabHeap<Derived>().Allocate(kernel);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Free(KernelCore& kernel, Derived* obj) {
|
static void Free(KernelCore& kernel, Derived* obj) {
|
||||||
|
|
|
@ -45,9 +45,25 @@ AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} {
|
||||||
{32, nullptr, "GetActiveOutputTarget"},
|
{32, nullptr, "GetActiveOutputTarget"},
|
||||||
{33, nullptr, "GetTargetDeviceInfo"},
|
{33, nullptr, "GetTargetDeviceInfo"},
|
||||||
{34, nullptr, "AcquireTargetNotification"},
|
{34, nullptr, "AcquireTargetNotification"},
|
||||||
|
{35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
||||||
|
{36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
||||||
|
{37, nullptr, "SetHearingProtectionSafeguardEnabled"},
|
||||||
|
{38, nullptr, "IsHearingProtectionSafeguardEnabled"},
|
||||||
|
{39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"},
|
||||||
|
{40, nullptr, "GetSystemInformationForDebug"},
|
||||||
|
{41, nullptr, "SetVolumeButtonLongPressTime"},
|
||||||
|
{42, nullptr, "SetNativeVolumeForDebug"},
|
||||||
{10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
|
{10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
|
||||||
{10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
|
{10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
|
||||||
{10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
|
{10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
|
||||||
|
{10100, nullptr, "GetAudioVolumeDataForPlayReport"},
|
||||||
|
{10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"},
|
||||||
|
{10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"},
|
||||||
|
{10103, nullptr, "GetAudioOutputTargetForPlayReport"},
|
||||||
|
{10104, nullptr, "GetAudioOutputChannelCountForPlayReport"},
|
||||||
|
{10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
|
||||||
|
{10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"},
|
||||||
|
{50000, nullptr, "SetAnalogInputBoostGainForPrototyping"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,8 @@ public:
|
||||||
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
|
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
|
||||||
{10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"},
|
{10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"},
|
||||||
{11, nullptr, "ExecuteAudioRendererRendering"},
|
{11, nullptr, "ExecuteAudioRendererRendering"},
|
||||||
|
{12, &IAudioRenderer::SetVoiceDropParameter, "SetVoiceDropParameter"},
|
||||||
|
{13, &IAudioRenderer::GetVoiceDropParameter, "GetVoiceDropParameter"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
@ -205,6 +207,30 @@ private:
|
||||||
LOG_DEBUG(Service_Audio, "called");
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetVoiceDropParameter(Kernel::HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
|
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
auto voice_drop_param{rp.Pop<f32>()};
|
||||||
|
|
||||||
|
auto& system_ = impl->GetSystem();
|
||||||
|
system_.SetVoiceDropParameter(voice_drop_param);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetVoiceDropParameter(Kernel::HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
|
|
||||||
|
auto& system_ = impl->GetSystem();
|
||||||
|
auto voice_drop_param{system_.GetVoiceDropParameter()};
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(voice_drop_param);
|
||||||
|
}
|
||||||
|
|
||||||
KernelHelpers::ServiceContext service_context;
|
KernelHelpers::ServiceContext service_context;
|
||||||
Kernel::KEvent* rendered_event;
|
Kernel::KEvent* rendered_event;
|
||||||
Manager& manager;
|
Manager& manager;
|
||||||
|
|
|
@ -15,9 +15,10 @@
|
||||||
namespace Service::SM {
|
namespace Service::SM {
|
||||||
|
|
||||||
void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
|
void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
|
||||||
ASSERT_MSG(!ctx.Session()->IsDomain(), "Session is already a domain");
|
ASSERT_MSG(!ctx.Session()->GetSessionRequestManager()->IsDomain(),
|
||||||
|
"Session is already a domain");
|
||||||
LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId());
|
LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId());
|
||||||
ctx.Session()->ConvertToDomain();
|
ctx.Session()->GetSessionRequestManager()->ConvertToDomainOnRequestEnd();
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
|
|
@ -156,12 +156,10 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
|
||||||
staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler),
|
staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler),
|
||||||
update_descriptor_queue(device, scheduler),
|
update_descriptor_queue(device, scheduler),
|
||||||
blit_image(device, scheduler, state_tracker, descriptor_pool),
|
blit_image(device, scheduler, state_tracker, descriptor_pool),
|
||||||
astc_decoder_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue,
|
|
||||||
memory_allocator),
|
|
||||||
render_pass_cache(device), texture_cache_runtime{device, scheduler,
|
render_pass_cache(device), texture_cache_runtime{device, scheduler,
|
||||||
memory_allocator, staging_pool,
|
memory_allocator, staging_pool,
|
||||||
blit_image, astc_decoder_pass,
|
blit_image, render_pass_cache,
|
||||||
render_pass_cache},
|
descriptor_pool, update_descriptor_queue},
|
||||||
texture_cache(texture_cache_runtime, *this),
|
texture_cache(texture_cache_runtime, *this),
|
||||||
buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool,
|
buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool,
|
||||||
update_descriptor_queue, descriptor_pool),
|
update_descriptor_queue, descriptor_pool),
|
||||||
|
|
|
@ -153,7 +153,6 @@ private:
|
||||||
DescriptorPool descriptor_pool;
|
DescriptorPool descriptor_pool;
|
||||||
UpdateDescriptorQueue update_descriptor_queue;
|
UpdateDescriptorQueue update_descriptor_queue;
|
||||||
BlitImageHelper blit_image;
|
BlitImageHelper blit_image;
|
||||||
ASTCDecoderPass astc_decoder_pass;
|
|
||||||
RenderPassCache render_pass_cache;
|
RenderPassCache render_pass_cache;
|
||||||
|
|
||||||
TextureCacheRuntime texture_cache_runtime;
|
TextureCacheRuntime texture_cache_runtime;
|
||||||
|
|
|
@ -791,12 +791,17 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
||||||
MemoryAllocator& memory_allocator_,
|
MemoryAllocator& memory_allocator_,
|
||||||
StagingBufferPool& staging_buffer_pool_,
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
BlitImageHelper& blit_image_helper_,
|
BlitImageHelper& blit_image_helper_,
|
||||||
ASTCDecoderPass& astc_decoder_pass_,
|
RenderPassCache& render_pass_cache_,
|
||||||
RenderPassCache& render_pass_cache_)
|
DescriptorPool& descriptor_pool,
|
||||||
|
UpdateDescriptorQueue& update_descriptor_queue)
|
||||||
: device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_},
|
: device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_},
|
||||||
staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_},
|
staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_},
|
||||||
astc_decoder_pass{astc_decoder_pass_}, render_pass_cache{render_pass_cache_},
|
render_pass_cache{render_pass_cache_}, resolution{Settings::values.resolution_info} {
|
||||||
resolution{Settings::values.resolution_info} {}
|
if (Settings::values.accelerate_astc) {
|
||||||
|
astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||||
|
update_descriptor_queue, memory_allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TextureCacheRuntime::Finish() {
|
void TextureCacheRuntime::Finish() {
|
||||||
scheduler.Finish();
|
scheduler.Finish();
|
||||||
|
@ -1845,7 +1850,7 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
||||||
Image& image, const StagingBufferRef& map,
|
Image& image, const StagingBufferRef& map,
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||||
if (IsPixelFormatASTC(image.info.format)) {
|
if (IsPixelFormatASTC(image.info.format)) {
|
||||||
return astc_decoder_pass.Assemble(image, map, swizzles);
|
return astc_decoder_pass->Assemble(image, map, swizzles);
|
||||||
}
|
}
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
#include "shader_recompiler/shader_info.h"
|
#include "shader_recompiler/shader_info.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||||
#include "video_core/texture_cache/image_view_base.h"
|
#include "video_core/texture_cache/image_view_base.h"
|
||||||
#include "video_core/texture_cache/texture_cache_base.h"
|
#include "video_core/texture_cache/texture_cache_base.h"
|
||||||
|
@ -25,14 +26,15 @@ using VideoCommon::RenderTargets;
|
||||||
using VideoCommon::SlotVector;
|
using VideoCommon::SlotVector;
|
||||||
using VideoCore::Surface::PixelFormat;
|
using VideoCore::Surface::PixelFormat;
|
||||||
|
|
||||||
class ASTCDecoderPass;
|
|
||||||
class BlitImageHelper;
|
class BlitImageHelper;
|
||||||
|
class DescriptorPool;
|
||||||
class Device;
|
class Device;
|
||||||
class Image;
|
class Image;
|
||||||
class ImageView;
|
class ImageView;
|
||||||
class Framebuffer;
|
class Framebuffer;
|
||||||
class RenderPassCache;
|
class RenderPassCache;
|
||||||
class StagingBufferPool;
|
class StagingBufferPool;
|
||||||
|
class UpdateDescriptorQueue;
|
||||||
class Scheduler;
|
class Scheduler;
|
||||||
|
|
||||||
class TextureCacheRuntime {
|
class TextureCacheRuntime {
|
||||||
|
@ -41,8 +43,9 @@ public:
|
||||||
MemoryAllocator& memory_allocator_,
|
MemoryAllocator& memory_allocator_,
|
||||||
StagingBufferPool& staging_buffer_pool_,
|
StagingBufferPool& staging_buffer_pool_,
|
||||||
BlitImageHelper& blit_image_helper_,
|
BlitImageHelper& blit_image_helper_,
|
||||||
ASTCDecoderPass& astc_decoder_pass_,
|
RenderPassCache& render_pass_cache_,
|
||||||
RenderPassCache& render_pass_cache_);
|
DescriptorPool& descriptor_pool,
|
||||||
|
UpdateDescriptorQueue& update_descriptor_queue);
|
||||||
|
|
||||||
void Finish();
|
void Finish();
|
||||||
|
|
||||||
|
@ -97,8 +100,8 @@ public:
|
||||||
MemoryAllocator& memory_allocator;
|
MemoryAllocator& memory_allocator;
|
||||||
StagingBufferPool& staging_buffer_pool;
|
StagingBufferPool& staging_buffer_pool;
|
||||||
BlitImageHelper& blit_image_helper;
|
BlitImageHelper& blit_image_helper;
|
||||||
ASTCDecoderPass& astc_decoder_pass;
|
|
||||||
RenderPassCache& render_pass_cache;
|
RenderPassCache& render_pass_cache;
|
||||||
|
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||||
const Settings::ResolutionScalingInfo& resolution;
|
const Settings::ResolutionScalingInfo& resolution;
|
||||||
|
|
||||||
constexpr static size_t indexing_slots = 8 * sizeof(size_t);
|
constexpr static size_t indexing_slots = 8 * sizeof(size_t);
|
||||||
|
|
|
@ -1895,6 +1895,8 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
||||||
case GameListOpenTarget::SaveData: {
|
case GameListOpenTarget::SaveData: {
|
||||||
open_target = tr("Save Data");
|
open_target = tr("Save Data");
|
||||||
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
|
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
|
||||||
|
auto vfs_nand_dir =
|
||||||
|
vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
|
||||||
|
|
||||||
if (has_user_save) {
|
if (has_user_save) {
|
||||||
// User save data
|
// User save data
|
||||||
|
@ -1921,15 +1923,15 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
||||||
ASSERT(user_id);
|
ASSERT(user_id);
|
||||||
|
|
||||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||||
*system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
|
*system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
|
||||||
program_id, user_id->AsU128(), 0);
|
FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0);
|
||||||
|
|
||||||
path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
|
path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
|
||||||
} else {
|
} else {
|
||||||
// Device save data
|
// Device save data
|
||||||
const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||||
*system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
|
*system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
|
||||||
program_id, {}, 0);
|
FileSys::SaveDataType::SaveData, program_id, {}, 0);
|
||||||
|
|
||||||
path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path);
|
path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue