Compare commits
40 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32f3b6b865 | ||
|
|
322349e8cc | ||
|
|
ebcee03b0c | ||
|
|
b70751ccb9 | ||
|
|
e48e9a406c | ||
|
|
0e15c68f54 | ||
|
|
b834c21894 | ||
|
|
d52ee6d0a7 | ||
|
|
dcfa1992ea | ||
|
|
b7f1095980 | ||
|
|
9aeada734d | ||
|
|
e87670ee48 | ||
|
|
1dbf71ceb3 | ||
|
|
9014861858 | ||
|
|
d1da7eb119 | ||
|
|
0832da3e40 | ||
|
|
3359e5ab70 | ||
|
|
4fbe4da911 | ||
|
|
4fb5ca80c0 | ||
|
|
5f75d97125 | ||
|
|
7791cc8c2e | ||
|
|
e8b2fd21d8 | ||
|
|
fbda5e9ec9 | ||
|
|
410ed82922 | ||
|
|
7afb7a9494 | ||
|
|
6694e11303 | ||
|
|
ab25d1fe9a | ||
|
|
5ec6a265bf | ||
|
|
994f497781 | ||
|
|
874be0e3e1 | ||
|
|
8758378dc4 | ||
|
|
102630f2b2 | ||
|
|
aaf262bfed | ||
|
|
bcaadac22c | ||
|
|
908d3c5679 | ||
|
|
9a4beac95a | ||
|
|
f5110340e6 | ||
|
|
70df449d0a | ||
|
|
24620bc4ea | ||
|
|
b178c9a349 |
@@ -66,6 +66,7 @@ else()
|
||||
-Wextra
|
||||
-Wmissing-declarations
|
||||
-Wno-attributes
|
||||
-Wno-invalid-offsetof
|
||||
-Wno-unused-parameter
|
||||
)
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
|
||||
return stream->GetTagsAndReleaseBuffers(max_count);
|
||||
}
|
||||
|
||||
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream) {
|
||||
return stream->GetTagsAndReleaseBuffers();
|
||||
}
|
||||
|
||||
void AudioOut::StartStream(StreamPtr stream) {
|
||||
stream->Play();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ public:
|
||||
/// Returns a vector of recently released buffers specified by tag for the specified stream
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
|
||||
|
||||
/// Returns a vector of all recently released buffers specified by tag for the specified stream
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream);
|
||||
|
||||
/// Starts an audio stream for playback
|
||||
void StartStream(StreamPtr stream);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/audio_out.h"
|
||||
@@ -14,6 +15,59 @@
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] static constexpr s16 ClampToS16(s32 value) {
|
||||
return static_cast<s16>(std::clamp(value, s32{std::numeric_limits<s16>::min()},
|
||||
s32{std::numeric_limits<s16>::max()}));
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr s16 Mix2To1(s16 l_channel, s16 r_channel) {
|
||||
// Mix 50% from left and 50% from right channel
|
||||
constexpr float l_mix_amount = 50.0f / 100.0f;
|
||||
constexpr float r_mix_amount = 50.0f / 100.0f;
|
||||
return ClampToS16(static_cast<s32>((static_cast<float>(l_channel) * l_mix_amount) +
|
||||
(static_cast<float>(r_channel) * r_mix_amount)));
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel,
|
||||
s16 fc_channel,
|
||||
[[maybe_unused]] s16 lf_channel,
|
||||
s16 bl_channel, s16 br_channel) {
|
||||
// Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
|
||||
// are mixed to be 36.94%
|
||||
|
||||
constexpr float front_mix_amount = 36.94f / 100.0f;
|
||||
constexpr float center_mix_amount = 26.12f / 100.0f;
|
||||
constexpr float back_mix_amount = 36.94f / 100.0f;
|
||||
|
||||
// Mix 50% from left and 50% from right channel
|
||||
const auto left = front_mix_amount * static_cast<float>(fl_channel) +
|
||||
center_mix_amount * static_cast<float>(fc_channel) +
|
||||
back_mix_amount * static_cast<float>(bl_channel);
|
||||
|
||||
const auto right = front_mix_amount * static_cast<float>(fr_channel) +
|
||||
center_mix_amount * static_cast<float>(fc_channel) +
|
||||
back_mix_amount * static_cast<float>(br_channel);
|
||||
|
||||
return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2WithCoefficients(
|
||||
s16 fl_channel, s16 fr_channel, s16 fc_channel, s16 lf_channel, s16 bl_channel, s16 br_channel,
|
||||
const std::array<float_le, 4>& coeff) {
|
||||
const auto left =
|
||||
static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
||||
static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[0];
|
||||
|
||||
const auto right =
|
||||
static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
||||
static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[0];
|
||||
|
||||
return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace AudioCore {
|
||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||
AudioCommon::AudioRendererParameter params,
|
||||
@@ -62,10 +116,6 @@ Stream::State AudioRenderer::GetStreamState() const {
|
||||
return stream->GetState();
|
||||
}
|
||||
|
||||
static constexpr s16 ClampToS16(s32 value) {
|
||||
return static_cast<s16>(std::clamp(value, -32768, 32767));
|
||||
}
|
||||
|
||||
ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params) {
|
||||
|
||||
@@ -104,8 +154,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
|
||||
}
|
||||
}
|
||||
|
||||
auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
|
||||
splitter_context, effect_context);
|
||||
const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
|
||||
splitter_context, effect_context);
|
||||
|
||||
if (mix_result.IsError()) {
|
||||
LOG_ERROR(Audio, "Failed to update mix parameters");
|
||||
@@ -194,20 +244,22 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
|
||||
if (channel_count == 1) {
|
||||
const auto sample = ClampToS16(mix_buffers[0][i]);
|
||||
buffer[i * stream_channel_count + 0] = sample;
|
||||
if (stream_channel_count > 1) {
|
||||
buffer[i * stream_channel_count + 1] = sample;
|
||||
|
||||
// Place sample in all channels
|
||||
for (u32 channel = 0; channel < stream_channel_count; channel++) {
|
||||
buffer[i * stream_channel_count + channel] = sample;
|
||||
}
|
||||
|
||||
if (stream_channel_count == 6) {
|
||||
buffer[i * stream_channel_count + 2] = sample;
|
||||
buffer[i * stream_channel_count + 4] = sample;
|
||||
buffer[i * stream_channel_count + 5] = sample;
|
||||
// Output stream has a LF channel, mute it!
|
||||
buffer[i * stream_channel_count + 3] = 0;
|
||||
}
|
||||
|
||||
} else if (channel_count == 2) {
|
||||
const auto l_sample = ClampToS16(mix_buffers[0][i]);
|
||||
const auto r_sample = ClampToS16(mix_buffers[1][i]);
|
||||
if (stream_channel_count == 1) {
|
||||
buffer[i * stream_channel_count + 0] = l_sample;
|
||||
buffer[i * stream_channel_count + 0] = Mix2To1(l_sample, r_sample);
|
||||
} else if (stream_channel_count == 2) {
|
||||
buffer[i * stream_channel_count + 0] = l_sample;
|
||||
buffer[i * stream_channel_count + 1] = r_sample;
|
||||
@@ -215,8 +267,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
buffer[i * stream_channel_count + 0] = l_sample;
|
||||
buffer[i * stream_channel_count + 1] = r_sample;
|
||||
|
||||
buffer[i * stream_channel_count + 2] =
|
||||
ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2);
|
||||
// Combine both left and right channels to the center channel
|
||||
buffer[i * stream_channel_count + 2] = Mix2To1(l_sample, r_sample);
|
||||
|
||||
buffer[i * stream_channel_count + 4] = l_sample;
|
||||
buffer[i * stream_channel_count + 5] = r_sample;
|
||||
@@ -231,17 +283,25 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
const auto br_sample = ClampToS16(mix_buffers[5][i]);
|
||||
|
||||
if (stream_channel_count == 1) {
|
||||
buffer[i * stream_channel_count + 0] = fc_sample;
|
||||
// Games seem to ignore the center channel half the time, we use the front left
|
||||
// and right channel for mixing as that's where majority of the audio goes
|
||||
buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample);
|
||||
} else if (stream_channel_count == 2) {
|
||||
buffer[i * stream_channel_count + 0] =
|
||||
static_cast<s16>(0.3694f * static_cast<float>(fl_sample) +
|
||||
0.2612f * static_cast<float>(fc_sample) +
|
||||
0.3694f * static_cast<float>(bl_sample));
|
||||
buffer[i * stream_channel_count + 1] =
|
||||
static_cast<s16>(0.3694f * static_cast<float>(fr_sample) +
|
||||
0.2612f * static_cast<float>(fc_sample) +
|
||||
0.3694f * static_cast<float>(br_sample));
|
||||
// Mix all channels into 2 channels
|
||||
if (sink_context.HasDownMixingCoefficients()) {
|
||||
const auto [left, right] = Mix6To2WithCoefficients(
|
||||
fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
|
||||
sink_context.GetDownmixCoefficients());
|
||||
buffer[i * stream_channel_count + 0] = left;
|
||||
buffer[i * stream_channel_count + 1] = right;
|
||||
} else {
|
||||
const auto [left, right] = Mix6To2(fl_sample, fr_sample, fc_sample,
|
||||
lf_sample, bl_sample, br_sample);
|
||||
buffer[i * stream_channel_count + 0] = left;
|
||||
buffer[i * stream_channel_count + 1] = right;
|
||||
}
|
||||
} else if (stream_channel_count == 6) {
|
||||
// Pass through
|
||||
buffer[i * stream_channel_count + 0] = fl_sample;
|
||||
buffer[i * stream_channel_count + 1] = fr_sample;
|
||||
buffer[i * stream_channel_count + 2] = fc_sample;
|
||||
@@ -259,7 +319,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
}
|
||||
|
||||
void AudioRenderer::ReleaseAndQueueBuffers() {
|
||||
const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)};
|
||||
const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
|
||||
for (const auto& tag : released_buffers) {
|
||||
QueueMixedBuffer(tag);
|
||||
}
|
||||
|
||||
@@ -36,16 +36,10 @@ class Memory;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
using DSPStateHolder = std::array<VoiceState*, 6>;
|
||||
using DSPStateHolder = std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>;
|
||||
|
||||
class AudioOut;
|
||||
|
||||
struct RendererInfo {
|
||||
u64_le elasped_frame_count{};
|
||||
INSERT_PADDING_WORDS(2);
|
||||
};
|
||||
static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
|
||||
|
||||
class AudioRenderer {
|
||||
public:
|
||||
AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||
@@ -53,14 +47,14 @@ public:
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
|
||||
~AudioRenderer();
|
||||
|
||||
ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params);
|
||||
[[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params);
|
||||
void QueueMixedBuffer(Buffer::Tag tag);
|
||||
void ReleaseAndQueueBuffers();
|
||||
u32 GetSampleRate() const;
|
||||
u32 GetSampleCount() const;
|
||||
u32 GetMixBufferCount() const;
|
||||
Stream::State GetStreamState() const;
|
||||
[[nodiscard]] u32 GetSampleRate() const;
|
||||
[[nodiscard]] u32 GetSampleCount() const;
|
||||
[[nodiscard]] u32 GetMixBufferCount() const;
|
||||
[[nodiscard]] Stream::State GetStreamState() const;
|
||||
|
||||
private:
|
||||
BehaviorInfo behavior_info{};
|
||||
|
||||
@@ -43,22 +43,22 @@ public:
|
||||
void ClearError();
|
||||
void UpdateFlags(u64_le dest_flags);
|
||||
void SetUserRevision(u32_le revision);
|
||||
u32_le GetUserRevision() const;
|
||||
u32_le GetProcessRevision() const;
|
||||
[[nodiscard]] u32_le GetUserRevision() const;
|
||||
[[nodiscard]] u32_le GetProcessRevision() const;
|
||||
|
||||
bool IsAdpcmLoopContextBugFixed() const;
|
||||
bool IsSplitterSupported() const;
|
||||
bool IsLongSizePreDelaySupported() const;
|
||||
bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
|
||||
bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
|
||||
bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
|
||||
bool IsElapsedFrameCountSupported() const;
|
||||
bool IsMemoryPoolForceMappingEnabled() const;
|
||||
bool IsFlushVoiceWaveBuffersSupported() const;
|
||||
bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
|
||||
bool IsVoicePitchAndSrcSkippedSupported() const;
|
||||
bool IsMixInParameterDirtyOnlyUpdateSupported() const;
|
||||
bool IsSplitterBugFixed() const;
|
||||
[[nodiscard]] bool IsAdpcmLoopContextBugFixed() const;
|
||||
[[nodiscard]] bool IsSplitterSupported() const;
|
||||
[[nodiscard]] bool IsLongSizePreDelaySupported() const;
|
||||
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
|
||||
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
|
||||
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
|
||||
[[nodiscard]] bool IsElapsedFrameCountSupported() const;
|
||||
[[nodiscard]] bool IsMemoryPoolForceMappingEnabled() const;
|
||||
[[nodiscard]] bool IsFlushVoiceWaveBuffersSupported() const;
|
||||
[[nodiscard]] bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
|
||||
[[nodiscard]] bool IsVoicePitchAndSrcSkippedSupported() const;
|
||||
[[nodiscard]] bool IsMixInParameterDirtyOnlyUpdateSupported() const;
|
||||
[[nodiscard]] bool IsSplitterBugFixed() const;
|
||||
void CopyErrorInfo(OutParams& dst);
|
||||
|
||||
private:
|
||||
|
||||
@@ -39,13 +39,13 @@ public:
|
||||
void PreCommand();
|
||||
void PostCommand();
|
||||
|
||||
s32* GetChannelMixBuffer(s32 channel);
|
||||
const s32* GetChannelMixBuffer(s32 channel) const;
|
||||
s32* GetMixBuffer(std::size_t index);
|
||||
const s32* GetMixBuffer(std::size_t index) const;
|
||||
std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
||||
[[nodiscard]] s32* GetChannelMixBuffer(s32 channel);
|
||||
[[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const;
|
||||
[[nodiscard]] s32* GetMixBuffer(std::size_t index);
|
||||
[[nodiscard]] const s32* GetMixBuffer(std::size_t index) const;
|
||||
[[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
||||
|
||||
std::size_t GetTotalMixBufferCount() const;
|
||||
[[nodiscard]] std::size_t GetTotalMixBufferCount() const;
|
||||
|
||||
private:
|
||||
void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
|
||||
@@ -73,7 +73,7 @@ private:
|
||||
void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||
void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||
ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
||||
[[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
||||
|
||||
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
|
||||
u32 sample_count, u32 write_offset, u32 write_count);
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr std::size_t MAX_CHANNEL_COUNT = 6;
|
||||
constexpr std::size_t MAX_WAVE_BUFFERS = 4;
|
||||
constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
|
||||
constexpr u32 STREAM_SAMPLE_RATE = 48000;
|
||||
constexpr u32 STREAM_NUM_CHANNELS = 6;
|
||||
constexpr u32 STREAM_NUM_CHANNELS = 2;
|
||||
constexpr s32 NO_SPLITTER = -1;
|
||||
constexpr s32 NO_MIX = 0x7fffffff;
|
||||
constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
|
||||
|
||||
@@ -189,11 +189,11 @@ public:
|
||||
|
||||
virtual void Update(EffectInfo::InParams& in_params) = 0;
|
||||
virtual void UpdateForCommandGeneration() = 0;
|
||||
UsageState GetUsage() const;
|
||||
EffectType GetType() const;
|
||||
bool IsEnabled() const;
|
||||
s32 GetMixID() const;
|
||||
s32 GetProcessingOrder() const;
|
||||
[[nodiscard]] UsageState GetUsage() const;
|
||||
[[nodiscard]] EffectType GetType() const;
|
||||
[[nodiscard]] bool IsEnabled() const;
|
||||
[[nodiscard]] s32 GetMixID() const;
|
||||
[[nodiscard]] s32 GetProcessingOrder() const;
|
||||
|
||||
protected:
|
||||
UsageState usage{UsageState::Invalid};
|
||||
@@ -257,10 +257,10 @@ public:
|
||||
|
||||
void Update(EffectInfo::InParams& in_params) override;
|
||||
void UpdateForCommandGeneration() override;
|
||||
VAddr GetSendInfo() const;
|
||||
VAddr GetSendBuffer() const;
|
||||
VAddr GetRecvInfo() const;
|
||||
VAddr GetRecvBuffer() const;
|
||||
[[nodiscard]] VAddr GetSendInfo() const;
|
||||
[[nodiscard]] VAddr GetSendBuffer() const;
|
||||
[[nodiscard]] VAddr GetRecvInfo() const;
|
||||
[[nodiscard]] VAddr GetRecvBuffer() const;
|
||||
|
||||
private:
|
||||
VAddr send_info{};
|
||||
@@ -309,10 +309,10 @@ public:
|
||||
explicit EffectContext(std::size_t effect_count);
|
||||
~EffectContext();
|
||||
|
||||
std::size_t GetCount() const;
|
||||
EffectBase* GetInfo(std::size_t i);
|
||||
EffectBase* RetargetEffect(std::size_t i, EffectType effect);
|
||||
const EffectBase* GetInfo(std::size_t i) const;
|
||||
[[nodiscard]] std::size_t GetCount() const;
|
||||
[[nodiscard]] EffectBase* GetInfo(std::size_t i);
|
||||
[[nodiscard]] EffectBase* RetargetEffect(std::size_t i, EffectType effect);
|
||||
[[nodiscard]] const EffectBase* GetInfo(std::size_t i) const;
|
||||
|
||||
private:
|
||||
std::size_t effect_count{};
|
||||
|
||||
@@ -62,17 +62,17 @@ public:
|
||||
ServerMixInfo();
|
||||
~ServerMixInfo();
|
||||
|
||||
const ServerMixInfo::InParams& GetInParams() const;
|
||||
ServerMixInfo::InParams& GetInParams();
|
||||
[[nodiscard]] const ServerMixInfo::InParams& GetInParams() const;
|
||||
[[nodiscard]] ServerMixInfo::InParams& GetInParams();
|
||||
|
||||
bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
||||
BehaviorInfo& behavior_info, SplitterContext& splitter_context,
|
||||
EffectContext& effect_context);
|
||||
bool HasAnyConnection() const;
|
||||
[[nodiscard]] bool HasAnyConnection() const;
|
||||
void Cleanup();
|
||||
void SetEffectCount(std::size_t count);
|
||||
void ResetEffectProcessingOrder();
|
||||
s32 GetEffectOrder(std::size_t i) const;
|
||||
[[nodiscard]] s32 GetEffectOrder(std::size_t i) const;
|
||||
|
||||
private:
|
||||
std::vector<s32> effect_processing_order;
|
||||
@@ -91,15 +91,15 @@ public:
|
||||
void SortInfo();
|
||||
bool TsortInfo(SplitterContext& splitter_context);
|
||||
|
||||
std::size_t GetCount() const;
|
||||
ServerMixInfo& GetInfo(std::size_t i);
|
||||
const ServerMixInfo& GetInfo(std::size_t i) const;
|
||||
ServerMixInfo& GetSortedInfo(std::size_t i);
|
||||
const ServerMixInfo& GetSortedInfo(std::size_t i) const;
|
||||
ServerMixInfo& GetFinalMixInfo();
|
||||
const ServerMixInfo& GetFinalMixInfo() const;
|
||||
EdgeMatrix& GetEdgeMatrix();
|
||||
const EdgeMatrix& GetEdgeMatrix() const;
|
||||
[[nodiscard]] std::size_t GetCount() const;
|
||||
[[nodiscard]] ServerMixInfo& GetInfo(std::size_t i);
|
||||
[[nodiscard]] const ServerMixInfo& GetInfo(std::size_t i) const;
|
||||
[[nodiscard]] ServerMixInfo& GetSortedInfo(std::size_t i);
|
||||
[[nodiscard]] const ServerMixInfo& GetSortedInfo(std::size_t i) const;
|
||||
[[nodiscard]] ServerMixInfo& GetFinalMixInfo();
|
||||
[[nodiscard]] const ServerMixInfo& GetFinalMixInfo() const;
|
||||
[[nodiscard]] EdgeMatrix& GetEdgeMatrix();
|
||||
[[nodiscard]] const EdgeMatrix& GetEdgeMatrix() const;
|
||||
|
||||
private:
|
||||
void CalcMixBufferOffset();
|
||||
|
||||
@@ -12,10 +12,16 @@ std::size_t SinkContext::GetCount() const {
|
||||
return sink_count;
|
||||
}
|
||||
|
||||
void SinkContext::UpdateMainSink(SinkInfo::InParams& in) {
|
||||
void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) {
|
||||
ASSERT(in.type == SinkTypes::Device);
|
||||
|
||||
has_downmix_coefs = in.device.down_matrix_enabled;
|
||||
if (has_downmix_coefs) {
|
||||
downmix_coefficients = in.device.down_matrix_coef;
|
||||
}
|
||||
in_use = in.in_use;
|
||||
use_count = in.device.input_count;
|
||||
std::memcpy(buffers.data(), in.device.input.data(), AudioCommon::MAX_CHANNEL_COUNT);
|
||||
buffers = in.device.input;
|
||||
}
|
||||
|
||||
bool SinkContext::InUse() const {
|
||||
@@ -28,4 +34,12 @@ std::vector<u8> SinkContext::OutputBuffers() const {
|
||||
return buffer_ret;
|
||||
}
|
||||
|
||||
bool SinkContext::HasDownMixingCoefficients() const {
|
||||
return has_downmix_coefs;
|
||||
}
|
||||
|
||||
const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const {
|
||||
return downmix_coefficients;
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
using DownmixCoefficients = std::array<float_le, 4>;
|
||||
|
||||
enum class SinkTypes : u8 {
|
||||
Invalid = 0,
|
||||
Device = 1,
|
||||
@@ -50,7 +52,7 @@ public:
|
||||
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
|
||||
INSERT_UNION_PADDING_BYTES(1);
|
||||
bool down_matrix_enabled;
|
||||
std::array<float_le, 4> down_matrix_coef;
|
||||
DownmixCoefficients down_matrix_coef;
|
||||
};
|
||||
static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
|
||||
|
||||
@@ -74,16 +76,21 @@ public:
|
||||
explicit SinkContext(std::size_t sink_count);
|
||||
~SinkContext();
|
||||
|
||||
std::size_t GetCount() const;
|
||||
[[nodiscard]] std::size_t GetCount() const;
|
||||
|
||||
void UpdateMainSink(SinkInfo::InParams& in);
|
||||
bool InUse() const;
|
||||
std::vector<u8> OutputBuffers() const;
|
||||
void UpdateMainSink(const SinkInfo::InParams& in);
|
||||
[[nodiscard]] bool InUse() const;
|
||||
[[nodiscard]] std::vector<u8> OutputBuffers() const;
|
||||
|
||||
[[nodiscard]] bool HasDownMixingCoefficients() const;
|
||||
[[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const;
|
||||
|
||||
private:
|
||||
bool in_use{false};
|
||||
s32 use_count{};
|
||||
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
|
||||
std::size_t sink_count{};
|
||||
bool has_downmix_coefs{false};
|
||||
DownmixCoefficients downmix_coefficients{};
|
||||
};
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -136,4 +136,14 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count)
|
||||
return tags;
|
||||
}
|
||||
|
||||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
|
||||
std::vector<Buffer::Tag> tags;
|
||||
tags.reserve(released_buffers.size());
|
||||
while (!released_buffers.empty()) {
|
||||
tags.push_back(released_buffers.front()->GetTag());
|
||||
released_buffers.pop();
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -57,37 +57,40 @@ public:
|
||||
bool QueueBuffer(BufferPtr&& buffer);
|
||||
|
||||
/// Returns true if the audio stream contains a buffer with the specified tag
|
||||
bool ContainsBuffer(Buffer::Tag tag) const;
|
||||
[[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const;
|
||||
|
||||
/// Returns a vector of recently released buffers specified by tag
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
||||
[[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
||||
|
||||
/// Returns a vector of all recently released buffers specified by tag
|
||||
[[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers();
|
||||
|
||||
void SetVolume(float volume);
|
||||
|
||||
float GetVolume() const {
|
||||
[[nodiscard]] float GetVolume() const {
|
||||
return game_volume;
|
||||
}
|
||||
|
||||
/// Returns true if the stream is currently playing
|
||||
bool IsPlaying() const {
|
||||
[[nodiscard]] bool IsPlaying() const {
|
||||
return state == State::Playing;
|
||||
}
|
||||
|
||||
/// Returns the number of queued buffers
|
||||
std::size_t GetQueueSize() const {
|
||||
[[nodiscard]] std::size_t GetQueueSize() const {
|
||||
return queued_buffers.size();
|
||||
}
|
||||
|
||||
/// Gets the sample rate
|
||||
u32 GetSampleRate() const {
|
||||
[[nodiscard]] u32 GetSampleRate() const {
|
||||
return sample_rate;
|
||||
}
|
||||
|
||||
/// Gets the number of channels
|
||||
u32 GetNumChannels() const;
|
||||
[[nodiscard]] u32 GetNumChannels() const;
|
||||
|
||||
/// Get the state
|
||||
State GetState() const;
|
||||
[[nodiscard]] State GetState() const;
|
||||
|
||||
private:
|
||||
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
||||
@@ -97,7 +100,7 @@ private:
|
||||
void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {});
|
||||
|
||||
/// Gets the number of core cycles when the specified buffer will be released
|
||||
std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
|
||||
[[nodiscard]] std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
|
||||
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
Format format; ///< Format of the stream
|
||||
|
||||
@@ -145,7 +145,7 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
LOG_DEBUG(Core, "initialized OK");
|
||||
|
||||
device_memory = std::make_unique<Core::DeviceMemory>();
|
||||
|
||||
@@ -187,7 +187,7 @@ struct System::Impl {
|
||||
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
|
||||
|
||||
Service::Init(service_manager, system);
|
||||
services = std::make_unique<Service::Services>(service_manager, system);
|
||||
GDBStub::DeferStart();
|
||||
|
||||
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
|
||||
@@ -208,9 +208,11 @@ struct System::Impl {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
||||
const std::string& filepath) {
|
||||
app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath));
|
||||
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath,
|
||||
std::size_t program_index) {
|
||||
app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
|
||||
program_index);
|
||||
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
return ResultStatus::ErrorGetLoader;
|
||||
@@ -296,7 +298,7 @@ struct System::Impl {
|
||||
|
||||
// Shutdown emulation session
|
||||
GDBStub::Shutdown();
|
||||
Service::Shutdown();
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
telemetry_session.reset();
|
||||
@@ -306,8 +308,8 @@ struct System::Impl {
|
||||
cpu_manager.Shutdown();
|
||||
|
||||
// Shutdown kernel and core timing
|
||||
kernel.Shutdown();
|
||||
core_timing.Shutdown();
|
||||
kernel.Shutdown();
|
||||
|
||||
// Close app loader
|
||||
app_loader.reset();
|
||||
@@ -398,6 +400,9 @@ struct System::Impl {
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
|
||||
/// Services
|
||||
std::unique_ptr<Service::Services> services;
|
||||
|
||||
/// Telemetry session for this emulation session
|
||||
std::unique_ptr<Core::TelemetrySession> telemetry_session;
|
||||
|
||||
@@ -413,6 +418,8 @@ struct System::Impl {
|
||||
bool is_multicore{};
|
||||
bool is_async_gpu{};
|
||||
|
||||
ExecuteProgramCallback execute_program_callback;
|
||||
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
||||
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{};
|
||||
};
|
||||
@@ -444,8 +451,13 @@ void System::InvalidateCpuInstructionCaches() {
|
||||
impl->kernel.InvalidateAllInstructionCaches();
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
|
||||
return impl->Load(*this, emu_window, filepath);
|
||||
void System::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
|
||||
std::size_t program_index) {
|
||||
return impl->Load(*this, emu_window, filepath, program_index);
|
||||
}
|
||||
|
||||
bool System::IsPoweredOn() const {
|
||||
@@ -632,7 +644,11 @@ const std::string& System::GetStatusDetails() const {
|
||||
return impl->status_details;
|
||||
}
|
||||
|
||||
Loader::AppLoader& System::GetAppLoader() const {
|
||||
Loader::AppLoader& System::GetAppLoader() {
|
||||
return *impl->app_loader;
|
||||
}
|
||||
|
||||
const Loader::AppLoader& System::GetAppLoader() const {
|
||||
return *impl->app_loader;
|
||||
}
|
||||
|
||||
@@ -748,14 +764,6 @@ const System::CurrentBuildProcessID& System::GetCurrentProcessBuildID() const {
|
||||
return impl->build_id;
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
||||
return impl->Init(*this, emu_window);
|
||||
}
|
||||
|
||||
void System::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
Service::SM::ServiceManager& System::ServiceManager() {
|
||||
return *impl->service_manager;
|
||||
}
|
||||
@@ -786,4 +794,16 @@ bool System::IsMulticore() const {
|
||||
return impl->is_multicore;
|
||||
}
|
||||
|
||||
void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
|
||||
impl->execute_program_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void System::ExecuteProgram(std::size_t program_index) {
|
||||
if (impl->execute_program_callback) {
|
||||
impl->execute_program_callback(program_index);
|
||||
} else {
|
||||
LOG_CRITICAL(Core, "execute_program_callback must be initialized by the frontend");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
179
src/core/core.h
179
src/core/core.h
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -144,19 +145,19 @@ public:
|
||||
* Run the OS and Application
|
||||
* This function will start emulation and run the relevant devices
|
||||
*/
|
||||
ResultStatus Run();
|
||||
[[nodiscard]] ResultStatus Run();
|
||||
|
||||
/**
|
||||
* Pause the OS and Application
|
||||
* This function will pause emulation and stop the relevant devices
|
||||
*/
|
||||
ResultStatus Pause();
|
||||
[[nodiscard]] ResultStatus Pause();
|
||||
|
||||
/**
|
||||
* Step the CPU one instruction
|
||||
* @return Result status, indicating whether or not the operation succeeded.
|
||||
*/
|
||||
ResultStatus SingleStep();
|
||||
[[nodiscard]] ResultStatus SingleStep();
|
||||
|
||||
/**
|
||||
* Invalidate the CPU instruction caches
|
||||
@@ -173,22 +174,24 @@ public:
|
||||
* @param emu_window Reference to the host-system window used for video output and keyboard
|
||||
* input.
|
||||
* @param filepath String path to the executable application to load on the host file system.
|
||||
* @param program_index Specifies the index within the container of the program to launch.
|
||||
* @returns ResultStatus code, indicating if the operation succeeded.
|
||||
*/
|
||||
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath);
|
||||
[[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
|
||||
std::size_t program_index = 0);
|
||||
|
||||
/**
|
||||
* Indicates if the emulated system is powered on (all subsystems initialized and able to run an
|
||||
* application).
|
||||
* @returns True if the emulated system is powered on, otherwise false.
|
||||
*/
|
||||
bool IsPoweredOn() const;
|
||||
[[nodiscard]] bool IsPoweredOn() const;
|
||||
|
||||
/// Gets a reference to the telemetry session for this emulation session.
|
||||
Core::TelemetrySession& TelemetrySession();
|
||||
[[nodiscard]] Core::TelemetrySession& TelemetrySession();
|
||||
|
||||
/// Gets a reference to the telemetry session for this emulation session.
|
||||
const Core::TelemetrySession& TelemetrySession() const;
|
||||
[[nodiscard]] const Core::TelemetrySession& TelemetrySession() const;
|
||||
|
||||
/// Prepare the core emulation for a reschedule
|
||||
void PrepareReschedule();
|
||||
@@ -197,185 +200,178 @@ public:
|
||||
void PrepareReschedule(u32 core_index);
|
||||
|
||||
/// Gets and resets core performance statistics
|
||||
PerfStatsResults GetAndResetPerfStats();
|
||||
[[nodiscard]] PerfStatsResults GetAndResetPerfStats();
|
||||
|
||||
/// Gets an ARM interface to the CPU core that is currently running
|
||||
ARM_Interface& CurrentArmInterface();
|
||||
[[nodiscard]] ARM_Interface& CurrentArmInterface();
|
||||
|
||||
/// Gets an ARM interface to the CPU core that is currently running
|
||||
const ARM_Interface& CurrentArmInterface() const;
|
||||
[[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
|
||||
|
||||
/// Gets the index of the currently running CPU core
|
||||
std::size_t CurrentCoreIndex() const;
|
||||
[[nodiscard]] std::size_t CurrentCoreIndex() const;
|
||||
|
||||
/// Gets the scheduler for the CPU core that is currently running
|
||||
Kernel::Scheduler& CurrentScheduler();
|
||||
[[nodiscard]] Kernel::Scheduler& CurrentScheduler();
|
||||
|
||||
/// Gets the scheduler for the CPU core that is currently running
|
||||
const Kernel::Scheduler& CurrentScheduler() const;
|
||||
[[nodiscard]] const Kernel::Scheduler& CurrentScheduler() const;
|
||||
|
||||
/// Gets the physical core for the CPU core that is currently running
|
||||
Kernel::PhysicalCore& CurrentPhysicalCore();
|
||||
[[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
|
||||
|
||||
/// Gets the physical core for the CPU core that is currently running
|
||||
const Kernel::PhysicalCore& CurrentPhysicalCore() const;
|
||||
[[nodiscard]] const Kernel::PhysicalCore& CurrentPhysicalCore() const;
|
||||
|
||||
/// Gets a reference to an ARM interface for the CPU core with the specified index
|
||||
ARM_Interface& ArmInterface(std::size_t core_index);
|
||||
[[nodiscard]] ARM_Interface& ArmInterface(std::size_t core_index);
|
||||
|
||||
/// Gets a const reference to an ARM interface from the CPU core with the specified index
|
||||
const ARM_Interface& ArmInterface(std::size_t core_index) const;
|
||||
[[nodiscard]] const ARM_Interface& ArmInterface(std::size_t core_index) const;
|
||||
|
||||
CpuManager& GetCpuManager();
|
||||
/// Gets a reference to the underlying CPU manager.
|
||||
[[nodiscard]] CpuManager& GetCpuManager();
|
||||
|
||||
const CpuManager& GetCpuManager() const;
|
||||
/// Gets a const reference to the underlying CPU manager
|
||||
[[nodiscard]] const CpuManager& GetCpuManager() const;
|
||||
|
||||
/// Gets a reference to the exclusive monitor
|
||||
ExclusiveMonitor& Monitor();
|
||||
[[nodiscard]] ExclusiveMonitor& Monitor();
|
||||
|
||||
/// Gets a constant reference to the exclusive monitor
|
||||
const ExclusiveMonitor& Monitor() const;
|
||||
[[nodiscard]] const ExclusiveMonitor& Monitor() const;
|
||||
|
||||
/// Gets a mutable reference to the system memory instance.
|
||||
Core::Memory::Memory& Memory();
|
||||
[[nodiscard]] Core::Memory::Memory& Memory();
|
||||
|
||||
/// Gets a constant reference to the system memory instance.
|
||||
const Core::Memory::Memory& Memory() const;
|
||||
[[nodiscard]] const Core::Memory::Memory& Memory() const;
|
||||
|
||||
/// Gets a mutable reference to the GPU interface
|
||||
Tegra::GPU& GPU();
|
||||
[[nodiscard]] Tegra::GPU& GPU();
|
||||
|
||||
/// Gets an immutable reference to the GPU interface.
|
||||
const Tegra::GPU& GPU() const;
|
||||
[[nodiscard]] const Tegra::GPU& GPU() const;
|
||||
|
||||
/// Gets a mutable reference to the renderer.
|
||||
VideoCore::RendererBase& Renderer();
|
||||
[[nodiscard]] VideoCore::RendererBase& Renderer();
|
||||
|
||||
/// Gets an immutable reference to the renderer.
|
||||
const VideoCore::RendererBase& Renderer() const;
|
||||
[[nodiscard]] const VideoCore::RendererBase& Renderer() const;
|
||||
|
||||
/// Gets the scheduler for the CPU core with the specified index
|
||||
Kernel::Scheduler& Scheduler(std::size_t core_index);
|
||||
[[nodiscard]] Kernel::Scheduler& Scheduler(std::size_t core_index);
|
||||
|
||||
/// Gets the scheduler for the CPU core with the specified index
|
||||
const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
|
||||
[[nodiscard]] const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
|
||||
|
||||
/// Gets the global scheduler
|
||||
Kernel::GlobalScheduler& GlobalScheduler();
|
||||
[[nodiscard]] Kernel::GlobalScheduler& GlobalScheduler();
|
||||
|
||||
/// Gets the global scheduler
|
||||
const Kernel::GlobalScheduler& GlobalScheduler() const;
|
||||
[[nodiscard]] const Kernel::GlobalScheduler& GlobalScheduler() const;
|
||||
|
||||
/// Gets the manager for the guest device memory
|
||||
Core::DeviceMemory& DeviceMemory();
|
||||
[[nodiscard]] Core::DeviceMemory& DeviceMemory();
|
||||
|
||||
/// Gets the manager for the guest device memory
|
||||
const Core::DeviceMemory& DeviceMemory() const;
|
||||
[[nodiscard]] const Core::DeviceMemory& DeviceMemory() const;
|
||||
|
||||
/// Provides a pointer to the current process
|
||||
Kernel::Process* CurrentProcess();
|
||||
[[nodiscard]] Kernel::Process* CurrentProcess();
|
||||
|
||||
/// Provides a constant pointer to the current process.
|
||||
const Kernel::Process* CurrentProcess() const;
|
||||
[[nodiscard]] const Kernel::Process* CurrentProcess() const;
|
||||
|
||||
/// Provides a reference to the core timing instance.
|
||||
Timing::CoreTiming& CoreTiming();
|
||||
[[nodiscard]] Timing::CoreTiming& CoreTiming();
|
||||
|
||||
/// Provides a constant reference to the core timing instance.
|
||||
const Timing::CoreTiming& CoreTiming() const;
|
||||
[[nodiscard]] const Timing::CoreTiming& CoreTiming() const;
|
||||
|
||||
/// Provides a reference to the interrupt manager instance.
|
||||
Core::Hardware::InterruptManager& InterruptManager();
|
||||
[[nodiscard]] Core::Hardware::InterruptManager& InterruptManager();
|
||||
|
||||
/// Provides a constant reference to the interrupt manager instance.
|
||||
const Core::Hardware::InterruptManager& InterruptManager() const;
|
||||
[[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const;
|
||||
|
||||
/// Provides a reference to the kernel instance.
|
||||
Kernel::KernelCore& Kernel();
|
||||
[[nodiscard]] Kernel::KernelCore& Kernel();
|
||||
|
||||
/// Provides a constant reference to the kernel instance.
|
||||
const Kernel::KernelCore& Kernel() const;
|
||||
[[nodiscard]] const Kernel::KernelCore& Kernel() const;
|
||||
|
||||
/// Provides a reference to the internal PerfStats instance.
|
||||
Core::PerfStats& GetPerfStats();
|
||||
[[nodiscard]] Core::PerfStats& GetPerfStats();
|
||||
|
||||
/// Provides a constant reference to the internal PerfStats instance.
|
||||
const Core::PerfStats& GetPerfStats() const;
|
||||
[[nodiscard]] const Core::PerfStats& GetPerfStats() const;
|
||||
|
||||
/// Provides a reference to the frame limiter;
|
||||
Core::FrameLimiter& FrameLimiter();
|
||||
[[nodiscard]] Core::FrameLimiter& FrameLimiter();
|
||||
|
||||
/// Provides a constant referent to the frame limiter
|
||||
const Core::FrameLimiter& FrameLimiter() const;
|
||||
[[nodiscard]] const Core::FrameLimiter& FrameLimiter() const;
|
||||
|
||||
/// Gets the name of the current game
|
||||
Loader::ResultStatus GetGameName(std::string& out) const;
|
||||
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
||||
|
||||
void SetStatus(ResultStatus new_status, const char* details);
|
||||
|
||||
const std::string& GetStatusDetails() const;
|
||||
[[nodiscard]] const std::string& GetStatusDetails() const;
|
||||
|
||||
Loader::AppLoader& GetAppLoader() const;
|
||||
[[nodiscard]] Loader::AppLoader& GetAppLoader();
|
||||
[[nodiscard]] const Loader::AppLoader& GetAppLoader() const;
|
||||
|
||||
Service::SM::ServiceManager& ServiceManager();
|
||||
const Service::SM::ServiceManager& ServiceManager() const;
|
||||
[[nodiscard]] Service::SM::ServiceManager& ServiceManager();
|
||||
[[nodiscard]] const Service::SM::ServiceManager& ServiceManager() const;
|
||||
|
||||
void SetFilesystem(FileSys::VirtualFilesystem vfs);
|
||||
|
||||
FileSys::VirtualFilesystem GetFilesystem() const;
|
||||
[[nodiscard]] FileSys::VirtualFilesystem GetFilesystem() const;
|
||||
|
||||
void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
|
||||
const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
|
||||
u64 main_region_size);
|
||||
|
||||
void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
|
||||
|
||||
void SetDefaultAppletFrontendSet();
|
||||
|
||||
Service::AM::Applets::AppletManager& GetAppletManager();
|
||||
|
||||
const Service::AM::Applets::AppletManager& GetAppletManager() const;
|
||||
[[nodiscard]] Service::AM::Applets::AppletManager& GetAppletManager();
|
||||
[[nodiscard]] const Service::AM::Applets::AppletManager& GetAppletManager() const;
|
||||
|
||||
void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
|
||||
|
||||
FileSys::ContentProvider& GetContentProvider();
|
||||
[[nodiscard]] FileSys::ContentProvider& GetContentProvider();
|
||||
[[nodiscard]] const FileSys::ContentProvider& GetContentProvider() const;
|
||||
|
||||
const FileSys::ContentProvider& GetContentProvider() const;
|
||||
|
||||
Service::FileSystem::FileSystemController& GetFileSystemController();
|
||||
|
||||
const Service::FileSystem::FileSystemController& GetFileSystemController() const;
|
||||
[[nodiscard]] Service::FileSystem::FileSystemController& GetFileSystemController();
|
||||
[[nodiscard]] const Service::FileSystem::FileSystemController& GetFileSystemController() const;
|
||||
|
||||
void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
|
||||
FileSys::ContentProvider* provider);
|
||||
|
||||
void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
|
||||
|
||||
const Reporter& GetReporter() const;
|
||||
[[nodiscard]] const Reporter& GetReporter() const;
|
||||
|
||||
Service::Glue::ARPManager& GetARPManager();
|
||||
[[nodiscard]] Service::Glue::ARPManager& GetARPManager();
|
||||
[[nodiscard]] const Service::Glue::ARPManager& GetARPManager() const;
|
||||
|
||||
const Service::Glue::ARPManager& GetARPManager() const;
|
||||
[[nodiscard]] Service::APM::Controller& GetAPMController();
|
||||
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
|
||||
|
||||
Service::APM::Controller& GetAPMController();
|
||||
[[nodiscard]] Service::LM::Manager& GetLogManager();
|
||||
[[nodiscard]] const Service::LM::Manager& GetLogManager() const;
|
||||
|
||||
const Service::APM::Controller& GetAPMController() const;
|
||||
|
||||
Service::LM::Manager& GetLogManager();
|
||||
|
||||
const Service::LM::Manager& GetLogManager() const;
|
||||
|
||||
Service::Time::TimeManager& GetTimeManager();
|
||||
|
||||
const Service::Time::TimeManager& GetTimeManager() const;
|
||||
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
||||
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
||||
|
||||
void SetExitLock(bool locked);
|
||||
|
||||
bool GetExitLock() const;
|
||||
[[nodiscard]] bool GetExitLock() const;
|
||||
|
||||
void SetCurrentProcessBuildID(const CurrentBuildProcessID& id);
|
||||
|
||||
const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
|
||||
[[nodiscard]] const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
|
||||
|
||||
/// Register a host thread as an emulated CPU Core.
|
||||
void RegisterCoreThread(std::size_t id);
|
||||
@@ -390,19 +386,28 @@ public:
|
||||
void ExitDynarmicProfile();
|
||||
|
||||
/// Tells if system is running on multicore.
|
||||
bool IsMulticore() const;
|
||||
[[nodiscard]] bool IsMulticore() const;
|
||||
|
||||
/// Type used for the frontend to designate a callback for System to re-launch the application
|
||||
/// using a specified program index.
|
||||
using ExecuteProgramCallback = std::function<void(std::size_t)>;
|
||||
|
||||
/**
|
||||
* Registers a callback from the frontend for System to re-launch the application using a
|
||||
* specified program index.
|
||||
* @param callback Callback from the frontend to relaunch the application.
|
||||
*/
|
||||
void RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback);
|
||||
|
||||
/**
|
||||
* Instructs the frontend to re-launch the application using the specified program_index.
|
||||
* @param program_index Specifies the index within the application of the program to launch.
|
||||
*/
|
||||
void ExecuteProgram(std::size_t program_index);
|
||||
|
||||
private:
|
||||
System();
|
||||
|
||||
/**
|
||||
* Initialize the emulated system.
|
||||
* @param emu_window Reference to the host-system window used for video output and keyboard
|
||||
* input.
|
||||
* @return ResultStatus code, indicating if the operation succeeded.
|
||||
*/
|
||||
ResultStatus Init(Frontend::EmuWindow& emu_window);
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/fiber.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
@@ -343,6 +344,16 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
data.initialized = true;
|
||||
const bool sc_sync = !is_async_gpu && !is_multicore;
|
||||
bool sc_sync_first_use = sc_sync;
|
||||
|
||||
// Cleanup
|
||||
SCOPE_EXIT({
|
||||
data.host_context->Exit();
|
||||
data.enter_barrier.reset();
|
||||
data.exit_barrier.reset();
|
||||
data.initialized = false;
|
||||
MicroProfileOnThreadExit();
|
||||
});
|
||||
|
||||
/// Running
|
||||
while (running_mode) {
|
||||
data.is_running = false;
|
||||
@@ -351,6 +362,12 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
system.GPU().ObtainContext();
|
||||
sc_sync_first_use = false;
|
||||
}
|
||||
|
||||
// Abort if emulation was killed before the session really starts
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& scheduler = system.Kernel().CurrentScheduler();
|
||||
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
|
||||
data.is_running = true;
|
||||
@@ -360,13 +377,6 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
data.exit_barrier->Wait();
|
||||
data.is_paused = false;
|
||||
}
|
||||
/// Time to cleanup
|
||||
data.host_context->Exit();
|
||||
data.enter_barrier.reset();
|
||||
data.exit_barrier.reset();
|
||||
data.initialized = false;
|
||||
|
||||
MicroProfileOnThreadExit();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -29,7 +29,7 @@ constexpr std::array partition_names{
|
||||
"logo",
|
||||
};
|
||||
|
||||
XCI::XCI(VirtualFile file_)
|
||||
XCI::XCI(VirtualFile file_, std::size_t program_index)
|
||||
: file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
|
||||
partitions(partition_names.size()),
|
||||
partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
|
||||
@@ -62,7 +62,8 @@ XCI::XCI(VirtualFile file_)
|
||||
}
|
||||
|
||||
secure_partition = std::make_shared<NSP>(
|
||||
main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
|
||||
main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]),
|
||||
program_index);
|
||||
|
||||
ncas = secure_partition->GetNCAsCollapsed();
|
||||
program =
|
||||
|
||||
@@ -78,7 +78,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
|
||||
|
||||
class XCI : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit XCI(VirtualFile file);
|
||||
explicit XCI(VirtualFile file, std::size_t program_index = 0);
|
||||
~XCI() override;
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
NSP::NSP(VirtualFile file_)
|
||||
: file(std::move(file_)), status{Loader::ResultStatus::Success},
|
||||
NSP::NSP(VirtualFile file_, std::size_t program_index)
|
||||
: file(std::move(file_)), program_index(program_index), status{Loader::ResultStatus::Success},
|
||||
pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
|
||||
if (pfs->GetStatus() != Loader::ResultStatus::Success) {
|
||||
status = pfs->GetStatus();
|
||||
@@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
|
||||
if (extracted)
|
||||
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
|
||||
|
||||
const auto title_id_iter = ncas.find(title_id);
|
||||
const auto title_id_iter = ncas.find(title_id + program_index);
|
||||
if (title_id_iter == ncas.end())
|
||||
return nullptr;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ enum class ContentRecordType : u8;
|
||||
|
||||
class NSP : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit NSP(VirtualFile file);
|
||||
explicit NSP(VirtualFile file, std::size_t program_index = 0);
|
||||
~NSP() override;
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
@@ -69,6 +69,8 @@ private:
|
||||
|
||||
VirtualFile file;
|
||||
|
||||
const std::size_t program_index;
|
||||
|
||||
bool extracted = false;
|
||||
Loader::ResultStatus status;
|
||||
std::map<u64, Loader::ResultStatus> program_status;
|
||||
|
||||
@@ -102,8 +102,8 @@ public:
|
||||
float render_surface_scale = 1.0f;
|
||||
};
|
||||
|
||||
/// Polls window events
|
||||
virtual void PollEvents() = 0;
|
||||
/// Called from GPU thread when a frame is displayed.
|
||||
virtual void OnFrameDisplayed() {}
|
||||
|
||||
/**
|
||||
* Returns a GraphicsContext that the frontend provides to be used for rendering.
|
||||
|
||||
@@ -681,7 +681,7 @@ static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
|
||||
}
|
||||
|
||||
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
||||
static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) {
|
||||
static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -246,9 +246,8 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
|
||||
|
||||
IDebugFunctions::~IDebugFunctions() = default;
|
||||
|
||||
ISelfController::ISelfController(Core::System& system,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) {
|
||||
ISelfController::ISelfController(Core::System& system, NVFlinger::NVFlinger& nvflinger)
|
||||
: ServiceFramework("ISelfController"), system(system), nvflinger(nvflinger) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISelfController::Exit, "Exit"},
|
||||
@@ -458,8 +457,8 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
|
||||
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just
|
||||
// create the layer in the Default display.
|
||||
const auto display_id = nvflinger->OpenDisplay("Default");
|
||||
const auto layer_id = nvflinger->CreateLayer(*display_id);
|
||||
const auto display_id = nvflinger.OpenDisplay("Default");
|
||||
const auto layer_id = nvflinger.CreateLayer(*display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -476,8 +475,8 @@ void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestConte
|
||||
// Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
|
||||
// side effects.
|
||||
// TODO: Support multiple layers
|
||||
const auto display_id = nvflinger->OpenDisplay("Default");
|
||||
const auto layer_id = nvflinger->CreateLayer(*display_id);
|
||||
const auto display_id = nvflinger.OpenDisplay("Default");
|
||||
const auto layer_id = nvflinger.CreateLayer(*display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -1189,9 +1188,9 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
{102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
|
||||
{110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
|
||||
{111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
|
||||
{120, nullptr, "ExecuteProgram"},
|
||||
{121, nullptr, "ClearUserChannel"},
|
||||
{122, nullptr, "UnpopToUserChannel"},
|
||||
{120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
|
||||
{121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
|
||||
{122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
|
||||
{123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
|
||||
{124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
|
||||
{130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
|
||||
@@ -1562,6 +1561,34 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::ExecuteProgram(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
[[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
|
||||
[[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
|
||||
const auto program_index = rp.Pop<u64>();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
system.ExecuteProgram(program_index);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::ClearUserChannel(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::UnpopToUserChannel(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
@@ -1586,8 +1613,8 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERe
|
||||
rb.PushCopyObjects(friend_invitation_storage_channel_event.readable);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
|
||||
Core::System& system) {
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel());
|
||||
// Needed on game boot
|
||||
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
||||
|
||||
@@ -121,8 +121,7 @@ public:
|
||||
|
||||
class ISelfController final : public ServiceFramework<ISelfController> {
|
||||
public:
|
||||
explicit ISelfController(Core::System& system_,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger_);
|
||||
explicit ISelfController(Core::System& system_, NVFlinger::NVFlinger& nvflinger_);
|
||||
~ISelfController() override;
|
||||
|
||||
private:
|
||||
@@ -156,7 +155,7 @@ private:
|
||||
};
|
||||
|
||||
Core::System& system;
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
NVFlinger::NVFlinger& nvflinger;
|
||||
Kernel::EventPair launchable_event;
|
||||
Kernel::EventPair accumulated_suspended_tick_changed_event;
|
||||
|
||||
@@ -288,6 +287,9 @@ private:
|
||||
void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
|
||||
void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
|
||||
void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
|
||||
void ExecuteProgram(Kernel::HLERequestContext& ctx);
|
||||
void ClearUserChannel(Kernel::HLERequestContext& ctx);
|
||||
void UnpopToUserChannel(Kernel::HLERequestContext& ctx);
|
||||
void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
|
||||
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
|
||||
void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
|
||||
@@ -332,7 +334,7 @@ public:
|
||||
};
|
||||
|
||||
/// Registers all AM services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
|
||||
Core::System& system);
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -13,10 +13,10 @@ namespace Service::AM {
|
||||
|
||||
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
|
||||
public:
|
||||
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
explicit ILibraryAppletProxy(NVFlinger::NVFlinger& nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue,
|
||||
Core::System& system)
|
||||
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
|
||||
: ServiceFramework("ILibraryAppletProxy"), nvflinger(nvflinger),
|
||||
msg_queue(std::move(msg_queue)), system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -109,16 +109,16 @@ private:
|
||||
rb.PushIpcInterface<IApplicationFunctions>(system);
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
NVFlinger::NVFlinger& nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
|
||||
public:
|
||||
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
explicit ISystemAppletProxy(NVFlinger::NVFlinger& nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
|
||||
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
|
||||
: ServiceFramework("ISystemAppletProxy"), nvflinger(nvflinger),
|
||||
msg_queue(std::move(msg_queue)), system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -220,7 +220,8 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationCreator>();
|
||||
}
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
|
||||
NVFlinger::NVFlinger& nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
Core::System& system;
|
||||
};
|
||||
@@ -249,10 +250,10 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system);
|
||||
}
|
||||
|
||||
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
|
||||
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)), system(system) {
|
||||
AppletAE::AppletAE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue,
|
||||
Core::System& system)
|
||||
: ServiceFramework("appletAE"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)),
|
||||
system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
|
||||
|
||||
@@ -23,7 +23,7 @@ class AppletMessageQueue;
|
||||
|
||||
class AppletAE final : public ServiceFramework<AppletAE> {
|
||||
public:
|
||||
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
explicit AppletAE(NVFlinger::NVFlinger& nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
|
||||
~AppletAE() override;
|
||||
|
||||
@@ -34,7 +34,7 @@ private:
|
||||
void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
|
||||
void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
NVFlinger::NVFlinger& nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace Service::AM {
|
||||
|
||||
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
|
||||
public:
|
||||
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
explicit IApplicationProxy(NVFlinger::NVFlinger& nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(nvflinger),
|
||||
msg_queue(std::move(msg_queue)), system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -98,7 +98,7 @@ private:
|
||||
rb.PushIpcInterface<IApplicationFunctions>(system);
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
NVFlinger::NVFlinger& nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
Core::System& system;
|
||||
};
|
||||
@@ -111,10 +111,10 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system);
|
||||
}
|
||||
|
||||
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
|
||||
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)), system(system) {
|
||||
AppletOE::AppletOE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue,
|
||||
Core::System& system)
|
||||
: ServiceFramework("appletOE"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)),
|
||||
system(system) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ class AppletMessageQueue;
|
||||
|
||||
class AppletOE final : public ServiceFramework<AppletOE> {
|
||||
public:
|
||||
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
explicit AppletOE(NVFlinger::NVFlinger& nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
|
||||
~AppletOE() override;
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
private:
|
||||
void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
NVFlinger::NVFlinger& nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -31,8 +31,8 @@ public:
|
||||
* @param output A buffer where the output data will be written to.
|
||||
* @returns The result code of the ioctl.
|
||||
*/
|
||||
virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output) = 0;
|
||||
virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) = 0;
|
||||
|
||||
/**
|
||||
* Handles an ioctl2 request.
|
||||
@@ -43,7 +43,8 @@ public:
|
||||
* @returns The result code of the ioctl.
|
||||
*/
|
||||
virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) = 0;
|
||||
|
||||
/**
|
||||
* Handles an ioctl3 request.
|
||||
@@ -54,7 +55,7 @@ public:
|
||||
* @returns The result code of the ioctl.
|
||||
*/
|
||||
virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) = 0;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) = 0;
|
||||
|
||||
protected:
|
||||
Core::System& system;
|
||||
|
||||
@@ -18,20 +18,21 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
|
||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvdisp_disp0 ::~nvdisp_disp0() = default;
|
||||
|
||||
NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output) {
|
||||
NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) {
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
@@ -20,11 +20,13 @@ public:
|
||||
explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvdisp_disp0() override;
|
||||
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) override;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
||||
|
||||
/// Performs a screen flip, drawing the buffer pointed to by the handle.
|
||||
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
|
||||
|
||||
@@ -21,8 +21,8 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_
|
||||
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
|
||||
nvhost_as_gpu::~nvhost_as_gpu() = default;
|
||||
|
||||
NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output) {
|
||||
NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 'A':
|
||||
switch (command.cmd) {
|
||||
@@ -55,13 +55,14 @@ NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
}
|
||||
|
||||
NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) {
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 'A':
|
||||
switch (command.cmd) {
|
||||
|
||||
@@ -30,11 +30,13 @@ public:
|
||||
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvhost_as_gpu() override;
|
||||
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) override;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
class BufferMap final {
|
||||
|
||||
@@ -20,7 +20,8 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
|
||||
: nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
|
||||
nvhost_ctrl::~nvhost_ctrl() = default;
|
||||
|
||||
NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 0x0:
|
||||
switch (command.cmd) {
|
||||
@@ -29,9 +30,9 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v
|
||||
case 0x1c:
|
||||
return IocCtrlClearEventWait(input, output);
|
||||
case 0x1d:
|
||||
return IocCtrlEventWait(input, output, false);
|
||||
return IocCtrlEventWait(input, output, false, ctrl);
|
||||
case 0x1e:
|
||||
return IocCtrlEventWait(input, output, true);
|
||||
return IocCtrlEventWait(input, output, true, ctrl);
|
||||
case 0x1f:
|
||||
return IocCtrlEventRegister(input, output);
|
||||
case 0x20:
|
||||
@@ -47,13 +48,14 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v
|
||||
}
|
||||
|
||||
NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) {
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
@@ -67,7 +69,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
|
||||
}
|
||||
|
||||
NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
bool is_async) {
|
||||
bool is_async, IoctlCtrl& ctrl) {
|
||||
IocCtrlEventWaitParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
|
||||
@@ -139,7 +141,10 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
|
||||
params.value |= event_id;
|
||||
event.event.writable->Clear();
|
||||
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
|
||||
if (!is_async) {
|
||||
if (!is_async && ctrl.fresh_call) {
|
||||
ctrl.must_delay = true;
|
||||
ctrl.timeout = params.timeout;
|
||||
ctrl.event_id = event_id;
|
||||
return NvResult::Timeout;
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
|
||||
@@ -18,11 +18,13 @@ public:
|
||||
SyncpointManager& syncpoint_manager);
|
||||
~nvhost_ctrl() override;
|
||||
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) override;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
struct IocSyncptReadParams {
|
||||
@@ -121,7 +123,8 @@ private:
|
||||
static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
|
||||
|
||||
NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
|
||||
NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
|
||||
IoctlCtrl& ctrl);
|
||||
NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
@@ -16,7 +16,7 @@ nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
|
||||
nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
|
||||
|
||||
NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output) {
|
||||
std::vector<u8>& output, IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 'G':
|
||||
switch (command.cmd) {
|
||||
@@ -48,13 +48,15 @@ NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
}
|
||||
|
||||
NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output, std::vector<u8>& inline_output) {
|
||||
std::vector<u8>& output, std::vector<u8>& inline_output,
|
||||
IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 'G':
|
||||
switch (command.cmd) {
|
||||
@@ -162,7 +164,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::
|
||||
params.gpu_characteristics_buf_size = 0xA0;
|
||||
params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
|
||||
|
||||
std::memcpy(output.data(), input.data(), output.size());
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
std::memcpy(inline_output.data(), ¶ms.gc, inline_output.size());
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@ public:
|
||||
explicit nvhost_ctrl_gpu(Core::System& system);
|
||||
~nvhost_ctrl_gpu() override;
|
||||
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) override;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
struct IoctlGpuCharacteristics {
|
||||
|
||||
@@ -23,7 +23,8 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
|
||||
|
||||
nvhost_gpu::~nvhost_gpu() = default;
|
||||
|
||||
NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 0x0:
|
||||
switch (command.cmd) {
|
||||
@@ -75,7 +76,8 @@ NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve
|
||||
};
|
||||
|
||||
NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 'H':
|
||||
switch (command.cmd) {
|
||||
@@ -89,7 +91,7 @@ NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
}
|
||||
|
||||
NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) {
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
@@ -26,11 +26,13 @@ public:
|
||||
SyncpointManager& syncpoint_manager);
|
||||
~nvhost_gpu() override;
|
||||
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) override;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
enum class CtxObjects : u32_le {
|
||||
|
||||
@@ -15,8 +15,8 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_de
|
||||
: nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
|
||||
nvhost_nvdec::~nvhost_nvdec() = default;
|
||||
|
||||
NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output) {
|
||||
NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 0x0:
|
||||
switch (command.cmd) {
|
||||
@@ -58,13 +58,14 @@ NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
}
|
||||
|
||||
NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) {
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
@@ -14,11 +14,13 @@ public:
|
||||
explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvhost_nvdec() override;
|
||||
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) override;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -25,8 +25,8 @@ public:
|
||||
* @param output A buffer where the output data will be written to.
|
||||
* @returns The result code of the ioctl.
|
||||
*/
|
||||
virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output) = 0;
|
||||
virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) = 0;
|
||||
|
||||
/**
|
||||
* Handles an ioctl2 request.
|
||||
@@ -37,7 +37,8 @@ public:
|
||||
* @returns The result code of the ioctl.
|
||||
*/
|
||||
virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) = 0;
|
||||
|
||||
/**
|
||||
* Handles an ioctl3 request.
|
||||
@@ -48,7 +49,7 @@ public:
|
||||
* @returns The result code of the ioctl.
|
||||
*/
|
||||
virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) = 0;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) = 0;
|
||||
|
||||
protected:
|
||||
class BufferMap final {
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices {
|
||||
nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
|
||||
nvhost_nvjpg::~nvhost_nvjpg() = default;
|
||||
|
||||
NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output) {
|
||||
NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 'H':
|
||||
switch (command.cmd) {
|
||||
@@ -33,13 +33,14 @@ NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
|
||||
}
|
||||
|
||||
NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) {
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@ public:
|
||||
explicit nvhost_nvjpg(Core::System& system);
|
||||
~nvhost_nvjpg() override;
|
||||
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) override;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
||||
|
||||
private:
|
||||
struct IoctlSetNvmapFD {
|
||||
|
||||
@@ -15,7 +15,8 @@ nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
|
||||
|
||||
nvhost_vic::~nvhost_vic() = default;
|
||||
|
||||
NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 0x0:
|
||||
switch (command.cmd) {
|
||||
@@ -50,13 +51,14 @@ NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve
|
||||
}
|
||||
|
||||
NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) {
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
@@ -14,10 +14,12 @@ public:
|
||||
explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvhost_vic();
|
||||
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) override;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
||||
};
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -19,7 +19,8 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) {
|
||||
|
||||
nvmap::~nvmap() = default;
|
||||
|
||||
NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
switch (command.group) {
|
||||
case 0x1:
|
||||
switch (command.cmd) {
|
||||
@@ -48,13 +49,14 @@ NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<
|
||||
}
|
||||
|
||||
NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) {
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
@@ -19,11 +19,13 @@ public:
|
||||
explicit nvmap(Core::System& system);
|
||||
~nvmap() override;
|
||||
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) override;
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) override;
|
||||
NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
|
||||
std::vector<u8>& inline_output) override;
|
||||
std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
|
||||
|
||||
/// Returns the allocated address of an nvmap object given its handle.
|
||||
VAddr GetObjectAddress(u32 handle) const;
|
||||
|
||||
@@ -61,10 +61,32 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
|
||||
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
|
||||
const auto input_buffer = ctx.ReadBuffer(0);
|
||||
|
||||
const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
|
||||
IoctlCtrl ctrl{};
|
||||
|
||||
if (command.is_out != 0) {
|
||||
ctx.WriteBuffer(output_buffer);
|
||||
const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer, ctrl);
|
||||
if (ctrl.must_delay) {
|
||||
ctrl.fresh_call = false;
|
||||
ctx.SleepClientThread(
|
||||
"NVServices::DelayedResponse", ctrl.timeout,
|
||||
[=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
|
||||
Kernel::ThreadWakeupReason reason) {
|
||||
IoctlCtrl ctrl2{ctrl};
|
||||
std::vector<u8> tmp_output = output_buffer;
|
||||
const auto nv_result2 = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output, ctrl2);
|
||||
|
||||
if (command.is_out != 0) {
|
||||
ctx.WriteBuffer(tmp_output);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx_, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(nv_result2);
|
||||
},
|
||||
nvdrv->GetEventWriteable(ctrl.event_id));
|
||||
} else {
|
||||
if (command.is_out != 0) {
|
||||
ctx.WriteBuffer(output_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -88,8 +110,35 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
|
||||
const auto input_inlined_buffer = ctx.ReadBuffer(1);
|
||||
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
|
||||
|
||||
IoctlCtrl ctrl{};
|
||||
|
||||
const auto nv_result =
|
||||
nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
|
||||
nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer, ctrl);
|
||||
if (ctrl.must_delay) {
|
||||
ctrl.fresh_call = false;
|
||||
ctx.SleepClientThread(
|
||||
"NVServices::DelayedResponse", ctrl.timeout,
|
||||
[=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
|
||||
Kernel::ThreadWakeupReason reason) {
|
||||
IoctlCtrl ctrl2{ctrl};
|
||||
std::vector<u8> tmp_output = output_buffer;
|
||||
const auto nv_result2 = nvdrv->Ioctl2(fd, command, input_buffer,
|
||||
input_inlined_buffer, tmp_output, ctrl2);
|
||||
|
||||
if (command.is_out != 0) {
|
||||
ctx.WriteBuffer(tmp_output);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx_, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(nv_result2);
|
||||
},
|
||||
nvdrv->GetEventWriteable(ctrl.event_id));
|
||||
} else {
|
||||
if (command.is_out != 0) {
|
||||
ctx.WriteBuffer(output_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.is_out != 0) {
|
||||
ctx.WriteBuffer(output_buffer);
|
||||
@@ -116,12 +165,36 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
|
||||
std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
|
||||
std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
|
||||
|
||||
IoctlCtrl ctrl{};
|
||||
const auto nv_result =
|
||||
nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
|
||||
nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline, ctrl);
|
||||
if (ctrl.must_delay) {
|
||||
ctrl.fresh_call = false;
|
||||
ctx.SleepClientThread(
|
||||
"NVServices::DelayedResponse", ctrl.timeout,
|
||||
[=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
|
||||
Kernel::ThreadWakeupReason reason) {
|
||||
IoctlCtrl ctrl2{ctrl};
|
||||
std::vector<u8> tmp_output = output_buffer;
|
||||
std::vector<u8> tmp_output2 = output_buffer;
|
||||
const auto nv_result2 =
|
||||
nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output2, ctrl2);
|
||||
|
||||
if (command.is_out != 0) {
|
||||
ctx.WriteBuffer(output_buffer, 0);
|
||||
ctx.WriteBuffer(output_buffer_inline, 1);
|
||||
if (command.is_out != 0) {
|
||||
ctx.WriteBuffer(tmp_output, 0);
|
||||
ctx.WriteBuffer(tmp_output2, 1);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx_, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(nv_result2);
|
||||
},
|
||||
nvdrv->GetEventWriteable(ctrl.event_id));
|
||||
} else {
|
||||
if (command.is_out != 0) {
|
||||
ctx.WriteBuffer(output_buffer, 0);
|
||||
ctx.WriteBuffer(output_buffer_inline, 1);
|
||||
}
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
@@ -97,4 +97,15 @@ union Ioctl {
|
||||
BitField<31, 1, u32> is_out;
|
||||
};
|
||||
|
||||
struct IoctlCtrl {
|
||||
// First call done to the servioce for services that call itself again after a call.
|
||||
bool fresh_call{true};
|
||||
// Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
|
||||
bool must_delay{};
|
||||
// Timeout for the delay
|
||||
s64 timeout{};
|
||||
// NV Event Id
|
||||
s32 event_id{-1};
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -91,7 +91,7 @@ DeviceFD Module::Open(const std::string& device_name) {
|
||||
}
|
||||
|
||||
NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output) {
|
||||
std::vector<u8>& output, IoctlCtrl& ctrl) {
|
||||
if (fd < 0) {
|
||||
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
|
||||
return NvResult::InvalidState;
|
||||
@@ -104,11 +104,12 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
return itr->second->Ioctl1(command, input, output);
|
||||
return itr->second->Ioctl1(command, input, output, ctrl);
|
||||
}
|
||||
|
||||
NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output) {
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output,
|
||||
IoctlCtrl& ctrl) {
|
||||
if (fd < 0) {
|
||||
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
|
||||
return NvResult::InvalidState;
|
||||
@@ -121,11 +122,11 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
return itr->second->Ioctl2(command, input, inline_input, output);
|
||||
return itr->second->Ioctl2(command, input, inline_input, output, ctrl);
|
||||
}
|
||||
|
||||
NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output, std::vector<u8>& inline_output) {
|
||||
std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
|
||||
if (fd < 0) {
|
||||
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
|
||||
return NvResult::InvalidState;
|
||||
@@ -138,7 +139,7 @@ NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input
|
||||
return NvResult::NotImplemented;
|
||||
}
|
||||
|
||||
return itr->second->Ioctl3(command, input, output, inline_output);
|
||||
return itr->second->Ioctl3(command, input, output, inline_output, ctrl);
|
||||
}
|
||||
|
||||
NvResult Module::Close(DeviceFD fd) {
|
||||
|
||||
@@ -119,13 +119,13 @@ public:
|
||||
|
||||
/// Sends an ioctl command to the specified file descriptor.
|
||||
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output);
|
||||
std::vector<u8>& output, IoctlCtrl& ctrl);
|
||||
|
||||
NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output);
|
||||
const std::vector<u8>& inline_input, std::vector<u8>& output, IoctlCtrl& ctrl);
|
||||
|
||||
NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output, std::vector<u8>& inline_output);
|
||||
std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl);
|
||||
|
||||
/// Closes a device file descriptor and returns operation success.
|
||||
NvResult Close(DeviceFD fd);
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
* @returns The lower bound for the specified syncpoint.
|
||||
*/
|
||||
u32 GetSyncpointMin(u32 syncpoint_id) const {
|
||||
return syncpoints[syncpoint_id].min.load(std::memory_order_relaxed);
|
||||
return syncpoints.at(syncpoint_id).min.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
* @returns The upper bound for the specified syncpoint.
|
||||
*/
|
||||
u32 GetSyncpointMax(u32 syncpoint_id) const {
|
||||
return syncpoints[syncpoint_id].max.load(std::memory_order_relaxed);
|
||||
return syncpoints.at(syncpoint_id).max.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -188,17 +188,19 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Initialize ServiceManager
|
||||
void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
|
||||
/// Initialize Services
|
||||
Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
|
||||
: nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} {
|
||||
|
||||
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
|
||||
// here and pass it into the respective InstallInterfaces functions.
|
||||
auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system);
|
||||
|
||||
system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
|
||||
|
||||
SM::ServiceManager::InstallInterfaces(sm, system.Kernel());
|
||||
|
||||
Account::InstallInterfaces(system);
|
||||
AM::InstallInterfaces(*sm, nv_flinger, system);
|
||||
AM::InstallInterfaces(*sm, *nv_flinger, system);
|
||||
AOC::InstallInterfaces(*sm, system);
|
||||
APM::InstallInterfaces(system);
|
||||
Audio::InstallInterfaces(*sm, system);
|
||||
@@ -246,14 +248,10 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
|
||||
SSL::InstallInterfaces(*sm);
|
||||
Time::InstallInterfaces(system);
|
||||
USB::InstallInterfaces(*sm);
|
||||
VI::InstallInterfaces(*sm, nv_flinger);
|
||||
VI::InstallInterfaces(*sm, *nv_flinger);
|
||||
WLAN::InstallInterfaces(*sm);
|
||||
|
||||
LOG_DEBUG(Service, "initialized OK");
|
||||
}
|
||||
|
||||
/// Shutdown ServiceManager
|
||||
void Shutdown() {
|
||||
LOG_DEBUG(Service, "shutdown OK");
|
||||
}
|
||||
Services::~Services() = default;
|
||||
|
||||
} // namespace Service
|
||||
|
||||
@@ -29,7 +29,11 @@ namespace Service {
|
||||
|
||||
namespace FileSystem {
|
||||
class FileSystemController;
|
||||
} // namespace FileSystem
|
||||
}
|
||||
|
||||
namespace NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace SM {
|
||||
class ServiceManager;
|
||||
@@ -181,10 +185,17 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
/// Initialize ServiceManager
|
||||
void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
|
||||
/**
|
||||
* The purpose of this class is to own any objects that need to be shared across the other service
|
||||
* implementations. Will be torn down when the global system instance is shutdown.
|
||||
*/
|
||||
class Services final {
|
||||
public:
|
||||
explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
|
||||
~Services();
|
||||
|
||||
/// Shutdown ServiceManager
|
||||
void Shutdown();
|
||||
private:
|
||||
std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
};
|
||||
|
||||
} // namespace Service
|
||||
|
||||
@@ -492,8 +492,8 @@ private:
|
||||
|
||||
class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
|
||||
public:
|
||||
explicit IHOSBinderDriver(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework("IHOSBinderDriver"), nv_flinger(std::move(nv_flinger)) {
|
||||
explicit IHOSBinderDriver(NVFlinger::NVFlinger& nv_flinger)
|
||||
: ServiceFramework("IHOSBinderDriver"), nv_flinger(nv_flinger) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
|
||||
{1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
|
||||
@@ -530,8 +530,8 @@ private:
|
||||
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
|
||||
static_cast<u32>(transaction), flags);
|
||||
|
||||
const auto guard = nv_flinger->Lock();
|
||||
auto& buffer_queue = nv_flinger->FindBufferQueue(id);
|
||||
const auto guard = nv_flinger.Lock();
|
||||
auto& buffer_queue = nv_flinger.FindBufferQueue(id);
|
||||
|
||||
switch (transaction) {
|
||||
case TransactionId::Connect: {
|
||||
@@ -570,8 +570,8 @@ private:
|
||||
[=, this](std::shared_ptr<Kernel::Thread> thread,
|
||||
Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
|
||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
||||
const auto guard = nv_flinger->Lock();
|
||||
auto& buffer_queue = nv_flinger->FindBufferQueue(id);
|
||||
const auto guard = nv_flinger.Lock();
|
||||
auto& buffer_queue = nv_flinger.FindBufferQueue(id);
|
||||
auto result = buffer_queue.DequeueBuffer(width, height);
|
||||
ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
|
||||
|
||||
@@ -676,7 +676,7 @@ private:
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
|
||||
|
||||
const auto& buffer_queue = nv_flinger->FindBufferQueue(id);
|
||||
const auto& buffer_queue = nv_flinger.FindBufferQueue(id);
|
||||
|
||||
// TODO(Subv): Find out what this actually is.
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
@@ -684,8 +684,8 @@ private:
|
||||
rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent());
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
}; // namespace VI
|
||||
NVFlinger::NVFlinger& nv_flinger;
|
||||
};
|
||||
|
||||
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
|
||||
public:
|
||||
@@ -790,8 +790,8 @@ private:
|
||||
|
||||
class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
|
||||
public:
|
||||
explicit IManagerDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) {
|
||||
explicit IManagerDisplayService(NVFlinger::NVFlinger& nv_flinger)
|
||||
: ServiceFramework("IManagerDisplayService"), nv_flinger(nv_flinger) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{200, nullptr, "AllocateProcessHeapBlock"},
|
||||
@@ -893,7 +893,7 @@ private:
|
||||
"(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
|
||||
unknown, display, aruid);
|
||||
|
||||
const auto layer_id = nv_flinger->CreateLayer(display);
|
||||
const auto layer_id = nv_flinger.CreateLayer(display);
|
||||
if (!layer_id) {
|
||||
LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -930,12 +930,12 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
NVFlinger::NVFlinger& nv_flinger;
|
||||
};
|
||||
|
||||
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
|
||||
public:
|
||||
explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
explicit IApplicationDisplayService(NVFlinger::NVFlinger& nv_flinger);
|
||||
|
||||
private:
|
||||
enum class ConvertedScaleMode : u64 {
|
||||
@@ -1010,7 +1010,7 @@ private:
|
||||
|
||||
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
|
||||
|
||||
const auto display_id = nv_flinger->OpenDisplay(name);
|
||||
const auto display_id = nv_flinger.OpenDisplay(name);
|
||||
if (!display_id) {
|
||||
LOG_ERROR(Service_VI, "Display not found! display_name={}", name);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -1110,7 +1110,7 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
|
||||
|
||||
const auto display_id = nv_flinger->OpenDisplay(display_name);
|
||||
const auto display_id = nv_flinger.OpenDisplay(display_name);
|
||||
if (!display_id) {
|
||||
LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -1118,7 +1118,7 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
const auto buffer_queue_id = nv_flinger->FindBufferQueueId(*display_id, layer_id);
|
||||
const auto buffer_queue_id = nv_flinger.FindBufferQueueId(*display_id, layer_id);
|
||||
if (!buffer_queue_id) {
|
||||
LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -1138,7 +1138,7 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id);
|
||||
|
||||
nv_flinger->CloseLayer(layer_id);
|
||||
nv_flinger.CloseLayer(layer_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -1154,7 +1154,7 @@ private:
|
||||
|
||||
// TODO(Subv): What's the difference between a Stray and a Managed layer?
|
||||
|
||||
const auto layer_id = nv_flinger->CreateLayer(display_id);
|
||||
const auto layer_id = nv_flinger.CreateLayer(display_id);
|
||||
if (!layer_id) {
|
||||
LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -1162,7 +1162,7 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
const auto buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, *layer_id);
|
||||
const auto buffer_queue_id = nv_flinger.FindBufferQueueId(display_id, *layer_id);
|
||||
if (!buffer_queue_id) {
|
||||
LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -1193,7 +1193,7 @@ private:
|
||||
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
|
||||
|
||||
const auto vsync_event = nv_flinger->FindVsyncEvent(display_id);
|
||||
const auto vsync_event = nv_flinger.FindVsyncEvent(display_id);
|
||||
if (!vsync_event) {
|
||||
LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -1258,12 +1258,11 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
NVFlinger::NVFlinger& nv_flinger;
|
||||
};
|
||||
|
||||
IApplicationDisplayService::IApplicationDisplayService(
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework("IApplicationDisplayService"), nv_flinger(std::move(nv_flinger)) {
|
||||
IApplicationDisplayService::IApplicationDisplayService(NVFlinger::NVFlinger& nv_flinger)
|
||||
: ServiceFramework("IApplicationDisplayService"), nv_flinger(nv_flinger) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
|
||||
{101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
|
||||
@@ -1304,8 +1303,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger,
|
||||
void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, NVFlinger::NVFlinger& nv_flinger,
|
||||
Permission permission) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto policy = rp.PopEnum<Policy>();
|
||||
@@ -1319,11 +1317,10 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger));
|
||||
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nv_flinger) {
|
||||
std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
|
||||
std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
|
||||
|
||||
@@ -43,12 +43,11 @@ enum class Policy {
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission);
|
||||
void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, NVFlinger::NVFlinger& nv_flinger,
|
||||
Permission permission);
|
||||
} // namespace detail
|
||||
|
||||
/// Registers all VI services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nv_flinger);
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} {
|
||||
VI_M::VI_M(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:m"}, nv_flinger{nv_flinger} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{2, &VI_M::GetDisplayService, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
|
||||
@@ -18,13 +18,13 @@ namespace Service::VI {
|
||||
|
||||
class VI_M final : public ServiceFramework<VI_M> {
|
||||
public:
|
||||
explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
explicit VI_M(NVFlinger::NVFlinger& nv_flinger);
|
||||
~VI_M() override;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
NVFlinger::NVFlinger& nv_flinger;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} {
|
||||
VI_S::VI_S(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:s"}, nv_flinger{nv_flinger} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &VI_S::GetDisplayService, "GetDisplayService"},
|
||||
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
|
||||
@@ -18,13 +18,13 @@ namespace Service::VI {
|
||||
|
||||
class VI_S final : public ServiceFramework<VI_S> {
|
||||
public:
|
||||
explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
explicit VI_S(NVFlinger::NVFlinger& nv_flinger);
|
||||
~VI_S() override;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
NVFlinger::NVFlinger& nv_flinger;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
|
||||
VI_U::VI_U(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:u"}, nv_flinger{nv_flinger} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &VI_U::GetDisplayService, "GetDisplayService"},
|
||||
{1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
|
||||
@@ -18,13 +18,13 @@ namespace Service::VI {
|
||||
|
||||
class VI_U final : public ServiceFramework<VI_U> {
|
||||
public:
|
||||
explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
explicit VI_U(NVFlinger::NVFlinger& nv_flinger);
|
||||
~VI_U() override;
|
||||
|
||||
private:
|
||||
void GetDisplayService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
NVFlinger::NVFlinger& nv_flinger;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -198,10 +198,11 @@ AppLoader::~AppLoader() = default;
|
||||
* @param system The system context to use.
|
||||
* @param file The file to retrieve the loader for
|
||||
* @param type The file type
|
||||
* @param program_index Specifies the index within the container of the program to launch.
|
||||
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
|
||||
*/
|
||||
static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
|
||||
FileType type) {
|
||||
FileType type, std::size_t program_index) {
|
||||
switch (type) {
|
||||
// Standard ELF file format.
|
||||
case FileType::ELF:
|
||||
@@ -222,7 +223,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
|
||||
// NX XCI (nX Card Image) file format.
|
||||
case FileType::XCI:
|
||||
return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
|
||||
system.GetContentProvider());
|
||||
system.GetContentProvider(), program_index);
|
||||
|
||||
// NX NAX (NintendoAesXts) file format.
|
||||
case FileType::NAX:
|
||||
@@ -231,7 +232,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
|
||||
// NX NSP (Nintendo Submission Package) file format
|
||||
case FileType::NSP:
|
||||
return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
|
||||
system.GetContentProvider());
|
||||
system.GetContentProvider(), program_index);
|
||||
|
||||
// NX KIP (Kernel Internal Process) file format
|
||||
case FileType::KIP:
|
||||
@@ -246,7 +247,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file) {
|
||||
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
|
||||
std::size_t program_index) {
|
||||
FileType type = IdentifyFile(file);
|
||||
const FileType filename_type = GuessFromFilename(file->GetName());
|
||||
|
||||
@@ -260,7 +262,7 @@ std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile
|
||||
|
||||
LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
|
||||
|
||||
return GetFileLoader(system, std::move(file), type);
|
||||
return GetFileLoader(system, std::move(file), type, program_index);
|
||||
}
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -293,9 +293,11 @@ protected:
|
||||
*
|
||||
* @param system The system context.
|
||||
* @param file The bootable file.
|
||||
* @param program_index Specifies the index within the container of the program to launch.
|
||||
*
|
||||
* @return the best loader for this file.
|
||||
*/
|
||||
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file);
|
||||
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
|
||||
std::size_t program_index = 0);
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -23,8 +23,9 @@ namespace Loader {
|
||||
|
||||
AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider)
|
||||
: AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)),
|
||||
const FileSys::ContentProvider& content_provider,
|
||||
std::size_t program_index)
|
||||
: AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file, program_index)),
|
||||
title_id(nsp->GetProgramTitleID()) {
|
||||
|
||||
if (nsp->GetStatus() != ResultStatus::Success) {
|
||||
|
||||
@@ -28,7 +28,8 @@ class AppLoader_NSP final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NSP(FileSys::VirtualFile file,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider);
|
||||
const FileSys::ContentProvider& content_provider,
|
||||
std::size_t program_index);
|
||||
~AppLoader_NSP() override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,8 +22,9 @@ namespace Loader {
|
||||
|
||||
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider)
|
||||
: AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
|
||||
const FileSys::ContentProvider& content_provider,
|
||||
std::size_t program_index)
|
||||
: AppLoader(file), xci(std::make_unique<FileSys::XCI>(file, program_index)),
|
||||
nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
|
||||
if (xci->GetStatus() != ResultStatus::Success) {
|
||||
return;
|
||||
|
||||
@@ -28,7 +28,8 @@ class AppLoader_XCI final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_XCI(FileSys::VirtualFile file,
|
||||
const Service::FileSystem::FileSystemController& fsc,
|
||||
const FileSys::ContentProvider& content_provider);
|
||||
const FileSys::ContentProvider& content_provider,
|
||||
std::size_t program_index);
|
||||
~AppLoader_XCI() override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,8 +56,8 @@ else()
|
||||
-Werror=reorder
|
||||
-Werror=shadow
|
||||
-Werror=sign-compare
|
||||
-Werror=unused-but-set-parameter
|
||||
-Werror=unused-but-set-variable
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
-Werror=unused-variable
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <thread>
|
||||
#include "common/math_util.h"
|
||||
#include "input_common/analog_from_button.h"
|
||||
|
||||
namespace InputCommon {
|
||||
@@ -11,31 +15,104 @@ public:
|
||||
using Button = std::unique_ptr<Input::ButtonDevice>;
|
||||
|
||||
Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
|
||||
float modifier_scale_)
|
||||
float modifier_scale_, float modifier_angle_)
|
||||
: up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
|
||||
right(std::move(right_)), modifier(std::move(modifier_)),
|
||||
modifier_scale(modifier_scale_) {}
|
||||
right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
|
||||
modifier_angle(modifier_angle_) {
|
||||
update_thread = std::thread(&Analog::UpdateStatus, this);
|
||||
}
|
||||
|
||||
~Analog() override {
|
||||
update_thread_running = false;
|
||||
if (update_thread.joinable()) {
|
||||
update_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void MoveToDirection(bool enable, float to_angle) {
|
||||
if (!enable) {
|
||||
return;
|
||||
}
|
||||
constexpr float TAU = Common::PI * 2.0f;
|
||||
// Use wider angle to ease the transition.
|
||||
constexpr float aperture = TAU * 0.15f;
|
||||
const float top_limit = to_angle + aperture;
|
||||
const float bottom_limit = to_angle - aperture;
|
||||
|
||||
if ((angle > to_angle && angle <= top_limit) ||
|
||||
(angle + TAU > to_angle && angle + TAU <= top_limit)) {
|
||||
angle -= modifier_angle;
|
||||
if (angle < 0) {
|
||||
angle += TAU;
|
||||
}
|
||||
} else if ((angle >= bottom_limit && angle < to_angle) ||
|
||||
(angle - TAU >= bottom_limit && angle - TAU < to_angle)) {
|
||||
angle += modifier_angle;
|
||||
if (angle >= TAU) {
|
||||
angle -= TAU;
|
||||
}
|
||||
} else {
|
||||
angle = to_angle;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateStatus() {
|
||||
while (update_thread_running) {
|
||||
const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
|
||||
|
||||
bool r = right->GetStatus();
|
||||
bool l = left->GetStatus();
|
||||
bool u = up->GetStatus();
|
||||
bool d = down->GetStatus();
|
||||
|
||||
// Eliminate contradictory movements
|
||||
if (r && l) {
|
||||
r = false;
|
||||
l = false;
|
||||
}
|
||||
if (u && d) {
|
||||
u = false;
|
||||
d = false;
|
||||
}
|
||||
|
||||
// Move to the right
|
||||
MoveToDirection(r && !u && !d, 0.0f);
|
||||
|
||||
// Move to the upper right
|
||||
MoveToDirection(r && u && !d, Common::PI * 0.25f);
|
||||
|
||||
// Move up
|
||||
MoveToDirection(u && !l && !r, Common::PI * 0.5f);
|
||||
|
||||
// Move to the upper left
|
||||
MoveToDirection(l && u && !d, Common::PI * 0.75f);
|
||||
|
||||
// Move to the left
|
||||
MoveToDirection(l && !u && !d, Common::PI);
|
||||
|
||||
// Move to the bottom left
|
||||
MoveToDirection(l && !u && d, Common::PI * 1.25f);
|
||||
|
||||
// Move down
|
||||
MoveToDirection(d && !l && !r, Common::PI * 1.5f);
|
||||
|
||||
// Move to the bottom right
|
||||
MoveToDirection(r && !u && d, Common::PI * 1.75f);
|
||||
|
||||
// Move if a key is pressed
|
||||
if (r || l || u || d) {
|
||||
amplitude = coef;
|
||||
} else {
|
||||
amplitude = 0;
|
||||
}
|
||||
|
||||
// Delay the update rate to 100hz
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<float, float> GetStatus() const override {
|
||||
constexpr float SQRT_HALF = 0.707106781f;
|
||||
int x = 0, y = 0;
|
||||
|
||||
if (right->GetStatus()) {
|
||||
++x;
|
||||
}
|
||||
if (left->GetStatus()) {
|
||||
--x;
|
||||
}
|
||||
if (up->GetStatus()) {
|
||||
++y;
|
||||
}
|
||||
if (down->GetStatus()) {
|
||||
--y;
|
||||
}
|
||||
|
||||
const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
|
||||
return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF),
|
||||
static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
|
||||
return std::make_tuple(std::cos(angle) * amplitude, std::sin(angle) * amplitude);
|
||||
}
|
||||
|
||||
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
|
||||
@@ -59,6 +136,11 @@ private:
|
||||
Button right;
|
||||
Button modifier;
|
||||
float modifier_scale;
|
||||
float modifier_angle;
|
||||
float angle{};
|
||||
float amplitude{};
|
||||
std::thread update_thread;
|
||||
bool update_thread_running{true};
|
||||
};
|
||||
|
||||
std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
|
||||
@@ -69,8 +151,10 @@ std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::Para
|
||||
auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
|
||||
auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
|
||||
auto modifier_scale = params.Get("modifier_scale", 0.5f);
|
||||
auto modifier_angle = params.Get("modifier_angle", 0.035f);
|
||||
return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
|
||||
std::move(right), std::move(modifier), modifier_scale);
|
||||
std::move(right), std::move(modifier), modifier_scale,
|
||||
modifier_angle);
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
||||
@@ -299,8 +299,8 @@ public:
|
||||
return gcadapter->RumblePlay(port, 0);
|
||||
}
|
||||
|
||||
bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, [[maybe_unused]] f32 amp_high,
|
||||
f32 freq_high) const override {
|
||||
bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
|
||||
[[maybe_unused]] f32 freq_high) const override {
|
||||
const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
|
||||
const auto processed_amplitude =
|
||||
static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
|
||||
|
||||
@@ -95,22 +95,29 @@ void GPU::WaitFence(u32 syncpoint_id, u32 value) {
|
||||
if (!is_async) {
|
||||
return;
|
||||
}
|
||||
if (syncpoint_id == UINT32_MAX) {
|
||||
// TODO: Research what this does.
|
||||
LOG_ERROR(HW_GPU, "Waiting for syncpoint -1 not implemented");
|
||||
return;
|
||||
}
|
||||
MICROPROFILE_SCOPE(GPU_wait);
|
||||
std::unique_lock lock{sync_mutex};
|
||||
sync_cv.wait(lock, [=, this] { return syncpoints[syncpoint_id].load() >= value; });
|
||||
sync_cv.wait(lock, [=, this] { return syncpoints.at(syncpoint_id).load() >= value; });
|
||||
}
|
||||
|
||||
void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
|
||||
syncpoints[syncpoint_id]++;
|
||||
auto& syncpoint = syncpoints.at(syncpoint_id);
|
||||
syncpoint++;
|
||||
std::lock_guard lock{sync_mutex};
|
||||
sync_cv.notify_all();
|
||||
if (!syncpt_interrupts[syncpoint_id].empty()) {
|
||||
u32 value = syncpoints[syncpoint_id].load();
|
||||
auto it = syncpt_interrupts[syncpoint_id].begin();
|
||||
while (it != syncpt_interrupts[syncpoint_id].end()) {
|
||||
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
|
||||
if (!interrupt.empty()) {
|
||||
u32 value = syncpoint.load();
|
||||
auto it = interrupt.begin();
|
||||
while (it != interrupt.end()) {
|
||||
if (value >= *it) {
|
||||
TriggerCpuInterrupt(syncpoint_id, *it);
|
||||
it = syncpt_interrupts[syncpoint_id].erase(it);
|
||||
it = interrupt.erase(it);
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
@@ -119,22 +126,22 @@ void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
|
||||
}
|
||||
|
||||
u32 GPU::GetSyncpointValue(const u32 syncpoint_id) const {
|
||||
return syncpoints[syncpoint_id].load();
|
||||
return syncpoints.at(syncpoint_id).load();
|
||||
}
|
||||
|
||||
void GPU::RegisterSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
|
||||
auto& interrupt = syncpt_interrupts[syncpoint_id];
|
||||
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
|
||||
bool contains = std::any_of(interrupt.begin(), interrupt.end(),
|
||||
[value](u32 in_value) { return in_value == value; });
|
||||
if (contains) {
|
||||
return;
|
||||
}
|
||||
syncpt_interrupts[syncpoint_id].emplace_back(value);
|
||||
interrupt.emplace_back(value);
|
||||
}
|
||||
|
||||
bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
|
||||
std::lock_guard lock{sync_mutex};
|
||||
auto& interrupt = syncpt_interrupts[syncpoint_id];
|
||||
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
|
||||
const auto iter =
|
||||
std::find_if(interrupt.begin(), interrupt.end(),
|
||||
[value](u32 interrupt_value) { return value == interrupt_value; });
|
||||
|
||||
@@ -151,8 +151,8 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
|
||||
rasterizer->TickFrame();
|
||||
|
||||
render_window.PollEvents();
|
||||
context->SwapBuffers();
|
||||
render_window.OnFrameDisplayed();
|
||||
}
|
||||
|
||||
void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/cityhash.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
|
||||
@@ -60,7 +61,13 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta
|
||||
rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
|
||||
topology.Assign(regs.draw.topology);
|
||||
|
||||
std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
|
||||
alpha_raw = 0;
|
||||
const auto test_func =
|
||||
regs.alpha_test_enabled == 1 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always;
|
||||
alpha_test_func.Assign(PackComparisonOp(test_func));
|
||||
alpha_test_ref = Common::BitCast<u32>(regs.alpha_test_ref);
|
||||
|
||||
point_size = Common::BitCast<u32>(regs.point_size);
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
binding_divisors[index] =
|
||||
|
||||
@@ -187,6 +187,13 @@ struct FixedPipelineState {
|
||||
BitField<23, 1, u32> rasterize_enable;
|
||||
BitField<24, 4, Maxwell::PrimitiveTopology> topology;
|
||||
};
|
||||
|
||||
u32 alpha_test_ref; ///< Alpha test reference value
|
||||
union {
|
||||
u32 alpha_raw;
|
||||
BitField<0, 3, u32> alpha_test_func;
|
||||
};
|
||||
|
||||
u32 point_size;
|
||||
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
|
||||
std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
|
||||
|
||||
@@ -252,8 +252,6 @@ RendererVulkan::~RendererVulkan() {
|
||||
}
|
||||
|
||||
void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
render_window.PollEvents();
|
||||
|
||||
if (!framebuffer) {
|
||||
return;
|
||||
}
|
||||
@@ -283,7 +281,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
rasterizer->TickFrame();
|
||||
}
|
||||
|
||||
render_window.PollEvents();
|
||||
render_window.OnFrameDisplayed();
|
||||
}
|
||||
|
||||
bool RendererVulkan::Init() {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
@@ -344,6 +345,11 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
|
||||
}
|
||||
specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one;
|
||||
|
||||
// Alpha test
|
||||
specialization.alpha_test_func =
|
||||
FixedPipelineState::UnpackComparisonOp(fixed_state.alpha_test_func.Value());
|
||||
specialization.alpha_test_ref = Common::BitCast<float>(fixed_state.alpha_test_ref);
|
||||
|
||||
SPIRVProgram program;
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
|
||||
|
||||
@@ -2075,6 +2075,45 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
Id MaxwellToSpirvComparison(Maxwell::ComparisonOp compare_op, Id operand_1, Id operand_2) {
|
||||
using Compare = Maxwell::ComparisonOp;
|
||||
switch (compare_op) {
|
||||
case Compare::NeverOld:
|
||||
return v_false; // Never let the test pass
|
||||
case Compare::LessOld:
|
||||
return OpFOrdLessThan(t_bool, operand_1, operand_2);
|
||||
case Compare::EqualOld:
|
||||
return OpFOrdEqual(t_bool, operand_1, operand_2);
|
||||
case Compare::LessEqualOld:
|
||||
return OpFOrdLessThanEqual(t_bool, operand_1, operand_2);
|
||||
case Compare::GreaterOld:
|
||||
return OpFOrdGreaterThan(t_bool, operand_1, operand_2);
|
||||
case Compare::NotEqualOld:
|
||||
return OpFOrdNotEqual(t_bool, operand_1, operand_2);
|
||||
case Compare::GreaterEqualOld:
|
||||
return OpFOrdGreaterThanEqual(t_bool, operand_1, operand_2);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void AlphaTest(Id pointer) {
|
||||
if (specialization.alpha_test_func == Maxwell::ComparisonOp::AlwaysOld) {
|
||||
return;
|
||||
}
|
||||
const Id true_label = OpLabel();
|
||||
const Id discard_label = OpLabel();
|
||||
const Id alpha_reference = Constant(t_float, specialization.alpha_test_ref);
|
||||
const Id alpha_value = OpLoad(t_float, pointer);
|
||||
const Id condition =
|
||||
MaxwellToSpirvComparison(specialization.alpha_test_func, alpha_value, alpha_reference);
|
||||
|
||||
OpBranchConditional(condition, true_label, discard_label);
|
||||
AddLabel(discard_label);
|
||||
OpKill();
|
||||
AddLabel(true_label);
|
||||
}
|
||||
|
||||
void PreExit() {
|
||||
if (stage == ShaderType::Vertex && specialization.ndc_minus_one_to_one) {
|
||||
const u32 position_index = out_indices.position.value();
|
||||
@@ -2097,8 +2136,6 @@ private:
|
||||
UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0,
|
||||
"Sample mask write is unimplemented");
|
||||
|
||||
// TODO(Rodrigo): Alpha testing
|
||||
|
||||
// Write the color outputs using the data in the shader registers, disabled
|
||||
// rendertargets/components are skipped in the register assignment.
|
||||
u32 current_reg = 0;
|
||||
@@ -2110,6 +2147,9 @@ private:
|
||||
}
|
||||
const Id pointer = AccessElement(t_out_float, frag_colors[rt], component);
|
||||
OpStore(pointer, SafeGetRegister(current_reg));
|
||||
if (rt == 0 && component == 3) {
|
||||
AlphaTest(pointer);
|
||||
}
|
||||
++current_reg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,8 @@ struct Specialization final {
|
||||
std::bitset<Maxwell::NumVertexAttributes> enabled_attributes;
|
||||
std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{};
|
||||
bool ndc_minus_one_to_one{};
|
||||
float alpha_test_ref{};
|
||||
Maxwell::ComparisonOp alpha_test_func{};
|
||||
};
|
||||
// Old gcc versions don't consider this trivially copyable.
|
||||
// static_assert(std::is_trivially_copyable_v<Specialization>);
|
||||
|
||||
@@ -212,10 +212,10 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) {
|
||||
return 0;
|
||||
case TextureFormat::R8G24:
|
||||
if (component == 0) {
|
||||
return 8;
|
||||
return 24;
|
||||
}
|
||||
if (component == 1) {
|
||||
return 24;
|
||||
return 8;
|
||||
}
|
||||
return 0;
|
||||
case TextureFormat::R8G8:
|
||||
|
||||
@@ -72,40 +72,6 @@ bool IsControllerCompatible(Settings::ControllerType controller_type,
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps the controller type combobox index to Controller Type enum
|
||||
constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
default:
|
||||
return Settings::ControllerType::ProController;
|
||||
case 1:
|
||||
return Settings::ControllerType::DualJoyconDetached;
|
||||
case 2:
|
||||
return Settings::ControllerType::LeftJoycon;
|
||||
case 3:
|
||||
return Settings::ControllerType::RightJoycon;
|
||||
case 4:
|
||||
return Settings::ControllerType::Handheld;
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps the Controller Type enum to controller type combobox index
|
||||
constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
|
||||
switch (type) {
|
||||
case Settings::ControllerType::ProController:
|
||||
default:
|
||||
return 0;
|
||||
case Settings::ControllerType::DualJoyconDetached:
|
||||
return 1;
|
||||
case Settings::ControllerType::LeftJoycon:
|
||||
return 2;
|
||||
case Settings::ControllerType::RightJoycon:
|
||||
return 3;
|
||||
case Settings::ControllerType::Handheld:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QtControllerSelectorDialog::QtControllerSelectorDialog(
|
||||
@@ -184,6 +150,11 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
|
||||
// This avoids unintentionally changing the states of elements while loading them in.
|
||||
SetSupportedControllers();
|
||||
DisableUnsupportedPlayers();
|
||||
|
||||
for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
|
||||
SetEmulatedControllers(player_index);
|
||||
}
|
||||
|
||||
LoadConfiguration();
|
||||
|
||||
for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
|
||||
@@ -223,8 +194,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
|
||||
|
||||
if (i == 0) {
|
||||
connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this](int index) {
|
||||
UpdateDockedState(GetControllerTypeFromIndex(index) ==
|
||||
[this, i](int index) {
|
||||
UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
|
||||
Settings::ControllerType::Handheld);
|
||||
});
|
||||
}
|
||||
@@ -281,8 +252,8 @@ void QtControllerSelectorDialog::LoadConfiguration() {
|
||||
(index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
|
||||
player_groupboxes[index]->setChecked(connected);
|
||||
connected_controller_checkboxes[index]->setChecked(connected);
|
||||
emulated_controllers[index]->setCurrentIndex(
|
||||
GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type));
|
||||
emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType(
|
||||
Settings::values.players.GetValue()[index].controller_type, index));
|
||||
}
|
||||
|
||||
UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
|
||||
@@ -338,7 +309,7 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() {
|
||||
}
|
||||
|
||||
const auto compatible = IsControllerCompatible(
|
||||
GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex()),
|
||||
GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex(), index),
|
||||
parameters);
|
||||
|
||||
// If any controller is found to be incompatible, return false early.
|
||||
@@ -422,6 +393,63 @@ void QtControllerSelectorDialog::SetSupportedControllers() {
|
||||
}
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) {
|
||||
auto& pairs = index_controller_type_pairs[player_index];
|
||||
|
||||
pairs.clear();
|
||||
emulated_controllers[player_index]->clear();
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::ProController);
|
||||
emulated_controllers[player_index]->addItem(tr("Pro Controller"));
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::DualJoyconDetached);
|
||||
emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::LeftJoycon);
|
||||
emulated_controllers[player_index]->addItem(tr("Left Joycon"));
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::RightJoycon);
|
||||
emulated_controllers[player_index]->addItem(tr("Right Joycon"));
|
||||
|
||||
if (player_index == 0) {
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::Handheld);
|
||||
emulated_controllers[player_index]->addItem(tr("Handheld"));
|
||||
}
|
||||
}
|
||||
|
||||
Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
|
||||
int index, std::size_t player_index) const {
|
||||
const auto& pairs = index_controller_type_pairs[player_index];
|
||||
|
||||
const auto it = std::find_if(pairs.begin(), pairs.end(),
|
||||
[index](const auto& pair) { return pair.first == index; });
|
||||
|
||||
if (it == pairs.end()) {
|
||||
return Settings::ControllerType::ProController;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type,
|
||||
std::size_t player_index) const {
|
||||
const auto& pairs = index_controller_type_pairs[player_index];
|
||||
|
||||
const auto it = std::find_if(pairs.begin(), pairs.end(),
|
||||
[type](const auto& pair) { return pair.second == type; });
|
||||
|
||||
if (it == pairs.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return it->first;
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
|
||||
if (!player_groupboxes[player_index]->isChecked()) {
|
||||
connected_controller_icons[player_index]->setStyleSheet(QString{});
|
||||
@@ -430,7 +458,8 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
|
||||
}
|
||||
|
||||
const QString stylesheet = [this, player_index] {
|
||||
switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex())) {
|
||||
switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
|
||||
player_index)) {
|
||||
case Settings::ControllerType::ProController:
|
||||
return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
|
||||
case Settings::ControllerType::DualJoyconDetached:
|
||||
@@ -446,6 +475,12 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
|
||||
}
|
||||
}();
|
||||
|
||||
if (stylesheet.isEmpty()) {
|
||||
connected_controller_icons[player_index]->setStyleSheet(QString{});
|
||||
player_labels[player_index]->show();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString theme = [] {
|
||||
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
|
||||
return QStringLiteral("_dark");
|
||||
@@ -463,8 +498,8 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
|
||||
void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
|
||||
auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
const auto controller_type =
|
||||
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex());
|
||||
const auto controller_type = GetControllerTypeFromIndex(
|
||||
emulated_controllers[player_index]->currentIndex(), player_index);
|
||||
const auto player_connected = player_groupboxes[player_index]->isChecked() &&
|
||||
controller_type != Settings::ControllerType::Handheld;
|
||||
|
||||
@@ -507,8 +542,8 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index)
|
||||
|
||||
void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
|
||||
if (!player_groupboxes[player_index]->isChecked() ||
|
||||
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()) ==
|
||||
Settings::ControllerType::Handheld) {
|
||||
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
|
||||
player_index) == Settings::ControllerType::Handheld) {
|
||||
led_patterns_boxes[player_index][0]->setChecked(false);
|
||||
led_patterns_boxes[player_index][1]->setChecked(false);
|
||||
led_patterns_boxes[player_index][2]->setChecked(false);
|
||||
|
||||
@@ -22,6 +22,10 @@ namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
namespace Settings {
|
||||
enum class ControllerType;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class QtControllerSelectorDialog;
|
||||
}
|
||||
@@ -57,6 +61,15 @@ private:
|
||||
// Sets the controller icons for "Supported Controller Types".
|
||||
void SetSupportedControllers();
|
||||
|
||||
// Sets the emulated controllers per player.
|
||||
void SetEmulatedControllers(std::size_t player_index);
|
||||
|
||||
// Gets the Controller Type for a given controller combobox index per player.
|
||||
Settings::ControllerType GetControllerTypeFromIndex(int index, std::size_t player_index) const;
|
||||
|
||||
// Gets the controller combobox index for a given Controller Type per player.
|
||||
int GetIndexFromControllerType(Settings::ControllerType type, std::size_t player_index) const;
|
||||
|
||||
// Updates the controller icons per player.
|
||||
void UpdateControllerIcon(std::size_t player_index);
|
||||
|
||||
@@ -114,6 +127,10 @@ private:
|
||||
// Comboboxes with a list of emulated controllers per player.
|
||||
std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
|
||||
|
||||
/// Pairs of emulated controller index and Controller Type enum per player.
|
||||
std::array<std::vector<std::pair<int, Settings::ControllerType>>, NUM_PLAYERS>
|
||||
index_controller_type_pairs;
|
||||
|
||||
// Labels representing the number of connected controllers
|
||||
// above the "Connected Controllers" checkboxes.
|
||||
std::array<QLabel*, NUM_PLAYERS> connected_controller_labels;
|
||||
|
||||
@@ -302,13 +302,19 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
||||
this->setMouseTracking(true);
|
||||
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void GRenderWindow::ExecuteProgram(std::size_t program_index) {
|
||||
emit ExecuteProgramSignal(program_index);
|
||||
}
|
||||
|
||||
GRenderWindow::~GRenderWindow() {
|
||||
input_subsystem->Shutdown();
|
||||
}
|
||||
|
||||
void GRenderWindow::PollEvents() {
|
||||
void GRenderWindow::OnFrameDisplayed() {
|
||||
if (!first_frame) {
|
||||
first_frame = true;
|
||||
emit FirstFrameDisplayed();
|
||||
|
||||
@@ -131,7 +131,7 @@ public:
|
||||
~GRenderWindow() override;
|
||||
|
||||
// EmuWindow implementation.
|
||||
void PollEvents() override;
|
||||
void OnFrameDisplayed() override;
|
||||
bool IsShown() const override;
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
@@ -166,6 +166,12 @@ public:
|
||||
|
||||
std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
|
||||
|
||||
/**
|
||||
* Instructs the window to re-launch the application using the specified program_index.
|
||||
* @param program_index Specifies the index within the application of the program to launch.
|
||||
*/
|
||||
void ExecuteProgram(std::size_t program_index);
|
||||
|
||||
public slots:
|
||||
void OnEmulationStarting(EmuThread* emu_thread);
|
||||
void OnEmulationStopping();
|
||||
@@ -175,6 +181,7 @@ signals:
|
||||
/// Emitted when the window is closed
|
||||
void Closed();
|
||||
void FirstFrameDisplayed();
|
||||
void ExecuteProgramSignal(std::size_t program_index);
|
||||
|
||||
private:
|
||||
void TouchBeginEvent(const QTouchEvent* event);
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include "yuzu/configuration/input_profiles.h"
|
||||
#include "yuzu/util/limitable_input_dialog.h"
|
||||
|
||||
using namespace Service::HID;
|
||||
|
||||
const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
|
||||
ConfigureInputPlayer::analog_sub_buttons{{
|
||||
"up",
|
||||
@@ -47,48 +49,12 @@ void UpdateController(Settings::ControllerType controller_type, std::size_t npad
|
||||
}
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
|
||||
auto& npad =
|
||||
sm.GetService<Service::HID::Hid>("hid")
|
||||
->GetAppletResource()
|
||||
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
|
||||
auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
|
||||
HidController::NPad);
|
||||
|
||||
npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
|
||||
}
|
||||
|
||||
/// Maps the controller type combobox index to Controller Type enum
|
||||
constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
default:
|
||||
return Settings::ControllerType::ProController;
|
||||
case 1:
|
||||
return Settings::ControllerType::DualJoyconDetached;
|
||||
case 2:
|
||||
return Settings::ControllerType::LeftJoycon;
|
||||
case 3:
|
||||
return Settings::ControllerType::RightJoycon;
|
||||
case 4:
|
||||
return Settings::ControllerType::Handheld;
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps the Controller Type enum to controller type combobox index
|
||||
constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
|
||||
switch (type) {
|
||||
case Settings::ControllerType::ProController:
|
||||
default:
|
||||
return 0;
|
||||
case Settings::ControllerType::DualJoyconDetached:
|
||||
return 1;
|
||||
case Settings::ControllerType::LeftJoycon:
|
||||
return 2;
|
||||
case Settings::ControllerType::RightJoycon:
|
||||
return 3;
|
||||
case Settings::ControllerType::Handheld:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
QString GetKeyName(int key_code) {
|
||||
switch (key_code) {
|
||||
case Qt::LeftButton:
|
||||
@@ -453,18 +419,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
connect(ui->groupConnectedController, &QGroupBox::toggled,
|
||||
[this](bool checked) { emit Connected(checked); });
|
||||
|
||||
// Set up controller type. Only Player 1 can choose Handheld.
|
||||
ui->comboControllerType->clear();
|
||||
|
||||
QStringList controller_types = {
|
||||
tr("Pro Controller"),
|
||||
tr("Dual Joycons"),
|
||||
tr("Left Joycon"),
|
||||
tr("Right Joycon"),
|
||||
};
|
||||
|
||||
if (player_index == 0) {
|
||||
controller_types.append(tr("Handheld"));
|
||||
connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this](int index) {
|
||||
emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
|
||||
@@ -480,12 +435,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
if (debug) {
|
||||
ui->buttonScreenshot->setEnabled(false);
|
||||
ui->buttonHome->setEnabled(false);
|
||||
QStringList debug_controller_types = {
|
||||
tr("Pro Controller"),
|
||||
};
|
||||
ui->comboControllerType->addItems(debug_controller_types);
|
||||
ui->comboControllerType->addItem(tr("Pro Controller"));
|
||||
} else {
|
||||
ui->comboControllerType->addItems(controller_types);
|
||||
SetConnectableControllers();
|
||||
}
|
||||
|
||||
UpdateControllerIcon();
|
||||
@@ -667,7 +619,7 @@ void ConfigureInputPlayer::LoadConfiguration() {
|
||||
return;
|
||||
}
|
||||
|
||||
ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
|
||||
ui->comboControllerType->setCurrentIndex(GetIndexFromControllerType(player.controller_type));
|
||||
ui->groupConnectedController->setChecked(
|
||||
player.connected ||
|
||||
(player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected));
|
||||
@@ -841,6 +793,82 @@ void ConfigureInputPlayer::UpdateUI() {
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::SetConnectableControllers() {
|
||||
const auto add_controllers = [this](bool enable_all,
|
||||
Controller_NPad::NpadStyleSet npad_style_set = {}) {
|
||||
index_controller_type_pairs.clear();
|
||||
ui->comboControllerType->clear();
|
||||
|
||||
if (enable_all || npad_style_set.pro_controller == 1) {
|
||||
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
|
||||
Settings::ControllerType::ProController);
|
||||
ui->comboControllerType->addItem(tr("Pro Controller"));
|
||||
}
|
||||
|
||||
if (enable_all || npad_style_set.joycon_dual == 1) {
|
||||
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
|
||||
Settings::ControllerType::DualJoyconDetached);
|
||||
ui->comboControllerType->addItem(tr("Dual Joycons"));
|
||||
}
|
||||
|
||||
if (enable_all || npad_style_set.joycon_left == 1) {
|
||||
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
|
||||
Settings::ControllerType::LeftJoycon);
|
||||
ui->comboControllerType->addItem(tr("Left Joycon"));
|
||||
}
|
||||
|
||||
if (enable_all || npad_style_set.joycon_right == 1) {
|
||||
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
|
||||
Settings::ControllerType::RightJoycon);
|
||||
ui->comboControllerType->addItem(tr("Right Joycon"));
|
||||
}
|
||||
|
||||
if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) {
|
||||
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
|
||||
Settings::ControllerType::Handheld);
|
||||
ui->comboControllerType->addItem(tr("Handheld"));
|
||||
}
|
||||
};
|
||||
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
|
||||
if (!system.IsPoweredOn()) {
|
||||
add_controllers(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
|
||||
auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
|
||||
HidController::NPad);
|
||||
|
||||
add_controllers(false, npad.GetSupportedStyleSet());
|
||||
}
|
||||
|
||||
Settings::ControllerType ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const {
|
||||
const auto it =
|
||||
std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
|
||||
[index](const auto& pair) { return pair.first == index; });
|
||||
|
||||
if (it == index_controller_type_pairs.end()) {
|
||||
return Settings::ControllerType::ProController;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType type) const {
|
||||
const auto it =
|
||||
std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
|
||||
[type](const auto& pair) { return pair.second == type; });
|
||||
|
||||
if (it == index_controller_type_pairs.end()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return it->first;
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::UpdateInputDevices() {
|
||||
input_devices = input_subsystem->GetInputDevices();
|
||||
ui->comboDevices->clear();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
@@ -112,6 +113,15 @@ private:
|
||||
/// Update UI to reflect current configuration.
|
||||
void UpdateUI();
|
||||
|
||||
/// Sets the available controllers.
|
||||
void SetConnectableControllers();
|
||||
|
||||
/// Gets the Controller Type for a given controller combobox index.
|
||||
Settings::ControllerType GetControllerTypeFromIndex(int index) const;
|
||||
|
||||
/// Gets the controller combobox index for a given Controller Type.
|
||||
int GetIndexFromControllerType(Settings::ControllerType type) const;
|
||||
|
||||
/// Update the available input devices.
|
||||
void UpdateInputDevices();
|
||||
|
||||
@@ -151,6 +161,9 @@ private:
|
||||
std::unique_ptr<QTimer> timeout_timer;
|
||||
std::unique_ptr<QTimer> poll_timer;
|
||||
|
||||
/// Stores a pair of "Connected Controllers" combobox index and Controller Type enum.
|
||||
std::vector<std::pair<int, Settings::ControllerType>> index_controller_type_pairs;
|
||||
|
||||
static constexpr int PLAYER_COUNT = 8;
|
||||
std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
|
||||
|
||||
|
||||
@@ -978,7 +978,7 @@ void GMainWindow::AllowOSSleep() {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GMainWindow::LoadROM(const QString& filename) {
|
||||
bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
|
||||
// Shutdown previous session if the emu thread is still active...
|
||||
if (emu_thread != nullptr)
|
||||
ShutdownGame();
|
||||
@@ -1003,7 +1003,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
|
||||
system.RegisterHostThread();
|
||||
|
||||
const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
|
||||
const Core::System::ResultStatus result{
|
||||
system.Load(*render_window, filename.toStdString(), program_index)};
|
||||
|
||||
const auto drd_callout =
|
||||
(UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
|
||||
@@ -1085,14 +1086,18 @@ void GMainWindow::SelectAndSetCurrentUser() {
|
||||
Settings::values.current_user = dialog.GetIndex();
|
||||
}
|
||||
|
||||
void GMainWindow::BootGame(const QString& filename) {
|
||||
void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
|
||||
LOG_INFO(Frontend, "yuzu starting...");
|
||||
StoreRecentFile(filename); // Put the filename on top of the list
|
||||
|
||||
u64 title_id{0};
|
||||
|
||||
last_filename_booted = filename;
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
|
||||
const auto loader = Loader::GetLoader(system, v_file);
|
||||
const auto loader = Loader::GetLoader(system, v_file, program_index);
|
||||
|
||||
if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
|
||||
// Load per game settings
|
||||
Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
|
||||
@@ -1106,7 +1111,7 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
SelectAndSetCurrentUser();
|
||||
}
|
||||
|
||||
if (!LoadROM(filename))
|
||||
if (!LoadROM(filename, program_index))
|
||||
return;
|
||||
|
||||
// Create and start the emulation thread
|
||||
@@ -1114,6 +1119,10 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
emit EmulationStarting(emu_thread.get());
|
||||
emu_thread->start();
|
||||
|
||||
// Register an ExecuteProgram callback such that Core can execute a sub-program
|
||||
system.RegisterExecuteProgramCallback(
|
||||
[this](std::size_t program_index) { render_window->ExecuteProgram(program_index); });
|
||||
|
||||
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
|
||||
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
|
||||
// before the CPU continues
|
||||
@@ -2136,6 +2145,11 @@ void GMainWindow::OnLoadComplete() {
|
||||
loading_screen->OnLoadComplete();
|
||||
}
|
||||
|
||||
void GMainWindow::OnExecuteProgram(std::size_t program_index) {
|
||||
ShutdownGame();
|
||||
BootGame(last_filename_booted, program_index);
|
||||
}
|
||||
|
||||
void GMainWindow::ErrorDisplayDisplayError(QString body) {
|
||||
QMessageBox::critical(this, tr("Error Display"), body);
|
||||
emit ErrorDisplayFinished();
|
||||
|
||||
@@ -131,6 +131,7 @@ signals:
|
||||
|
||||
public slots:
|
||||
void OnLoadComplete();
|
||||
void OnExecuteProgram(std::size_t program_index);
|
||||
void ControllerSelectorReconfigureControllers(
|
||||
const Core::Frontend::ControllerParameters& parameters);
|
||||
void ErrorDisplayDisplayError(QString body);
|
||||
@@ -154,8 +155,8 @@ private:
|
||||
void PreventOSSleep();
|
||||
void AllowOSSleep();
|
||||
|
||||
bool LoadROM(const QString& filename);
|
||||
void BootGame(const QString& filename);
|
||||
bool LoadROM(const QString& filename, std::size_t program_index);
|
||||
void BootGame(const QString& filename, std::size_t program_index = 0);
|
||||
void ShutdownGame();
|
||||
|
||||
void ShowTelemetryCallout();
|
||||
@@ -317,6 +318,9 @@ private:
|
||||
// Install progress dialog
|
||||
QProgressDialog* install_progress;
|
||||
|
||||
// Last game booted, used for multi-process apps
|
||||
QString last_filename_booted;
|
||||
|
||||
protected:
|
||||
void dropEvent(QDropEvent* event) override;
|
||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||
|
||||
@@ -121,62 +121,64 @@ void EmuWindow_SDL2::Fullscreen() {
|
||||
SDL_MaximizeWindow(render_window);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::PollEvents() {
|
||||
void EmuWindow_SDL2::WaitEvent() {
|
||||
// Called on main thread
|
||||
SDL_Event event;
|
||||
|
||||
// SDL_PollEvent returns 0 when there are no more events in the event queue
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_MINIMIZED:
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED;
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
is_open = false;
|
||||
break;
|
||||
}
|
||||
if (!SDL_WaitEvent(&event)) {
|
||||
LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
|
||||
case SDL_WINDOWEVENT_MINIMIZED:
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED;
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID)
|
||||
OnMouseMotion(event.motion.x, event.motion.y);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID) {
|
||||
OnMouseButton(event.button.button, event.button.state, event.button.x,
|
||||
event.button.y);
|
||||
}
|
||||
break;
|
||||
case SDL_FINGERDOWN:
|
||||
OnFingerDown(event.tfinger.x, event.tfinger.y);
|
||||
break;
|
||||
case SDL_FINGERMOTION:
|
||||
OnFingerMotion(event.tfinger.x, event.tfinger.y);
|
||||
break;
|
||||
case SDL_FINGERUP:
|
||||
OnFingerUp();
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
is_open = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID)
|
||||
OnMouseMotion(event.motion.x, event.motion.y);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID) {
|
||||
OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y);
|
||||
}
|
||||
break;
|
||||
case SDL_FINGERDOWN:
|
||||
OnFingerDown(event.tfinger.x, event.tfinger.y);
|
||||
break;
|
||||
case SDL_FINGERMOTION:
|
||||
OnFingerMotion(event.tfinger.x, event.tfinger.y);
|
||||
break;
|
||||
case SDL_FINGERUP:
|
||||
OnFingerUp();
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
is_open = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const u32 current_time = SDL_GetTicks();
|
||||
|
||||
@@ -23,38 +23,38 @@ public:
|
||||
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
|
||||
~EmuWindow_SDL2();
|
||||
|
||||
/// Polls window events
|
||||
void PollEvents() override;
|
||||
|
||||
/// Whether the window is still open, and a close request hasn't yet been sent
|
||||
bool IsOpen() const;
|
||||
|
||||
/// Returns if window is shown (not minimized)
|
||||
bool IsShown() const override;
|
||||
|
||||
/// Wait for the next event on the main thread.
|
||||
void WaitEvent();
|
||||
|
||||
protected:
|
||||
/// Called by PollEvents when a key is pressed or released.
|
||||
/// Called by WaitEvent when a key is pressed or released.
|
||||
void OnKeyEvent(int key, u8 state);
|
||||
|
||||
/// Called by PollEvents when the mouse moves.
|
||||
/// Called by WaitEvent when the mouse moves.
|
||||
void OnMouseMotion(s32 x, s32 y);
|
||||
|
||||
/// Called by PollEvents when a mouse button is pressed or released
|
||||
/// Called by WaitEvent when a mouse button is pressed or released
|
||||
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
|
||||
|
||||
/// Translates pixel position (0..1) to pixel positions
|
||||
std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
|
||||
|
||||
/// Called by PollEvents when a finger starts touching the touchscreen
|
||||
/// Called by WaitEvent when a finger starts touching the touchscreen
|
||||
void OnFingerDown(float x, float y);
|
||||
|
||||
/// Called by PollEvents when a finger moves while touching the touchscreen
|
||||
/// Called by WaitEvent when a finger moves while touching the touchscreen
|
||||
void OnFingerMotion(float x, float y);
|
||||
|
||||
/// Called by PollEvents when a finger stops touching the touchscreen
|
||||
/// Called by WaitEvent when a finger stops touching the touchscreen
|
||||
void OnFingerUp();
|
||||
|
||||
/// Called by PollEvents when any event that may cause the window to be resized occurs
|
||||
/// Called by WaitEvent when any event that may cause the window to be resized occurs
|
||||
void OnResize();
|
||||
|
||||
/// Called when user passes the fullscreen parameter flag
|
||||
|
||||
@@ -240,11 +240,11 @@ int main(int argc, char** argv) {
|
||||
system.CurrentProcess()->GetTitleID(), false,
|
||||
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
|
||||
|
||||
system.Run();
|
||||
void(system.Run());
|
||||
while (emu_window->IsOpen()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
emu_window->WaitEvent();
|
||||
}
|
||||
system.Pause();
|
||||
void(system.Pause());
|
||||
system.Shutdown();
|
||||
|
||||
detached_tasks.WaitForAllTasks();
|
||||
|
||||
@@ -109,8 +109,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_Hide::PollEvents() {}
|
||||
|
||||
bool EmuWindow_SDL2_Hide::IsShown() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,6 @@ public:
|
||||
explicit EmuWindow_SDL2_Hide();
|
||||
~EmuWindow_SDL2_Hide();
|
||||
|
||||
/// Polls window events
|
||||
void PollEvents() override;
|
||||
|
||||
/// Whether the screen is being shown or not.
|
||||
bool IsShown() const override;
|
||||
|
||||
|
||||
@@ -256,11 +256,11 @@ int main(int argc, char** argv) {
|
||||
|
||||
system.GPU().Start();
|
||||
|
||||
system.Run();
|
||||
void(system.Run());
|
||||
while (!finished) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
system.Pause();
|
||||
void(system.Pause());
|
||||
|
||||
detached_tasks.WaitForAllTasks();
|
||||
return return_value;
|
||||
|
||||
Reference in New Issue
Block a user