Compare commits

..

2 Commits

Author SHA1 Message Date
bunnei
cf944df166 Merge 309384da88 into 2b06301dbf 2018-08-04 18:37:23 +00:00
bunnei
309384da88 audren_u: Remove incorrect wave_buffer updates.
- Also some minor cleanups.
2018-07-31 23:16:25 -04:00
98 changed files with 3884 additions and 4706 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,13 +1,9 @@
add_library(audio_core STATIC
audio_out.cpp
audio_out.h
audio_renderer.cpp
audio_renderer.h
buffer.h
cubeb_sink.cpp
cubeb_sink.h
codec.cpp
codec.h
null_sink.h
stream.cpp
stream.h

View File

@@ -27,16 +27,16 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
return {};
}
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels,
Stream::ReleaseCallback&& release_callback) {
if (!sink) {
const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
sink = sink_details.factory(Settings::values.audio_device_id);
}
return std::make_shared<Stream>(
sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
std::move(release_callback),
sink->AcquireSinkStream(sample_rate, num_channels));
}
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
@@ -51,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) {
stream->Stop();
}
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
}

View File

@@ -5,7 +5,6 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "audio_core/buffer.h"
@@ -21,7 +20,7 @@ namespace AudioCore {
class AudioOut {
public:
/// Opens a new audio stream
StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
StreamPtr OpenStream(u32 sample_rate, u32 num_channels,
Stream::ReleaseCallback&& release_callback);
/// Returns a vector of recently released buffers specified by tag for the specified stream
@@ -34,7 +33,7 @@ public:
void StopStream(StreamPtr stream);
/// Queues a buffer into the specified audio stream, returns true on success
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
private:
SinkPtr sink;

View File

@@ -1,234 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "audio_core/audio_renderer.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/memory.h"
namespace AudioCore {
constexpr u32 STREAM_SAMPLE_RATE{48000};
constexpr u32 STREAM_NUM_CHANNELS{2};
AudioRenderer::AudioRenderer(AudioRendererParameter params,
Kernel::SharedPtr<Kernel::Event> buffer_event)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
audio_core = std::make_unique<AudioCore::AudioOut>();
stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
[=]() { buffer_event->Signal(); });
audio_core->StartStream(stream);
QueueMixedBuffer(0);
QueueMixedBuffer(1);
QueueMixedBuffer(2);
}
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};
std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
// Copy MemoryPoolInfo structs
std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
std::memcpy(mem_pool_info.data(),
input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
memory_pool_count * sizeof(MemoryPoolInfo));
// Copy VoiceInfo structs
size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
config.voice_resource_size};
for (auto& voice : voices) {
std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
offset += sizeof(VoiceInfo);
}
// Update voices
for (auto& voice : voices) {
voice.UpdateState();
if (!voice.GetInfo().is_in_use) {
continue;
}
if (voice.GetInfo().is_new) {
voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
}
}
// Update memory pool state
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
for (size_t index = 0; index < memory_pool.size(); ++index) {
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
memory_pool[index].state = MemoryPoolStates::Attached;
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
memory_pool[index].state = MemoryPoolStates::Detached;
}
}
// Release previous buffers and queue next ones for playback
ReleaseAndQueueBuffers();
// Copy output header
UpdateDataHeader response_data{worker_params};
std::vector<u8> output_params(response_data.total_size);
std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
// Copy output memory pool entries
std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
response_data.memory_pools_size);
// Copy output voice status
size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
for (const auto& voice : voices) {
std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
sizeof(VoiceOutStatus));
voice_out_status_offset += sizeof(VoiceOutStatus);
}
return output_params;
}
void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
wave_index = index & 3;
is_refresh_pending = true;
}
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
if (!IsPlaying()) {
return {};
}
if (is_refresh_pending) {
RefreshBuffer();
}
const size_t max_size{samples.size() - offset};
const size_t dequeue_offset{offset};
size_t size{sample_count * STREAM_NUM_CHANNELS};
if (size > max_size) {
size = max_size;
}
out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
offset += size;
const auto& wave_buffer{info.wave_buffer[wave_index]};
if (offset == samples.size()) {
offset = 0;
if (!wave_buffer.is_looping) {
SetWaveIndex(wave_index + 1);
}
out_status.wave_buffer_consumed++;
if (wave_buffer.end_of_stream) {
info.play_state = PlayState::Paused;
}
}
return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
}
void AudioRenderer::VoiceState::UpdateState() {
if (is_in_use && !info.is_in_use) {
// No longer in use, reset state
is_refresh_pending = true;
wave_index = 0;
offset = 0;
out_status = {};
}
is_in_use = info.is_in_use;
}
void AudioRenderer::VoiceState::RefreshBuffer() {
std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
info.wave_buffer[wave_index].buffer_sz);
switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
case Codec::PcmFormat::Int16: {
// PCM16 is played as-is
break;
}
case Codec::PcmFormat::Adpcm: {
// Decode ADPCM to PCM16
Codec::ADPCM_Coeff coeffs;
Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
new_samples.size() * sizeof(s16), coeffs, adpcm_state);
break;
}
default:
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
UNREACHABLE();
break;
}
switch (info.channel_count) {
case 1:
// 1 channel is upsampled to 2 channel
samples.resize(new_samples.size() * 2);
for (size_t index = 0; index < new_samples.size(); ++index) {
samples[index * 2] = new_samples[index];
samples[index * 2 + 1] = new_samples[index];
}
break;
case 2: {
// 2 channel is played as is
samples = std::move(new_samples);
break;
}
default:
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
UNREACHABLE();
break;
}
is_refresh_pending = false;
}
static constexpr s16 ClampToS16(s32 value) {
return static_cast<s16>(std::clamp(value, -32768, 32767));
}
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
constexpr size_t BUFFER_SIZE{512};
std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
for (auto& voice : voices) {
if (!voice.IsPlaying()) {
continue;
}
size_t offset{};
s64 samples_remaining{BUFFER_SIZE};
while (samples_remaining > 0) {
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
if (samples.empty()) {
break;
}
samples_remaining -= samples.size();
for (const auto& sample : samples) {
const s32 buffer_sample{buffer[offset]};
buffer[offset++] =
ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
}
}
}
audio_core->QueueBuffer(stream, tag, std::move(buffer));
}
void AudioRenderer::ReleaseAndQueueBuffers() {
const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
for (const auto& tag : released_buffers) {
QueueMixedBuffer(tag);
}
}
} // namespace AudioCore

View File

@@ -1,206 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <vector>
#include "audio_core/audio_out.h"
#include "audio_core/codec.h"
#include "audio_core/stream.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/kernel/event.h"
namespace AudioCore {
enum class PlayState : u8 {
Started = 0,
Stopped = 1,
Paused = 2,
};
struct AudioRendererParameter {
u32_le sample_rate;
u32_le sample_count;
u32_le unknown_8;
u32_le unknown_c;
u32_le voice_count;
u32_le sink_count;
u32_le effect_count;
u32_le unknown_1c;
u8 unknown_20;
INSERT_PADDING_BYTES(3);
u32_le splitter_count;
u32_le unknown_2c;
INSERT_PADDING_WORDS(1);
u32_le revision;
};
static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
enum class MemoryPoolStates : u32 { // Should be LE
Invalid = 0x0,
Unknown = 0x1,
RequestDetach = 0x2,
Detached = 0x3,
RequestAttach = 0x4,
Attached = 0x5,
Released = 0x6,
};
struct MemoryPoolEntry {
MemoryPoolStates state;
u32_le unknown_4;
u32_le unknown_8;
u32_le unknown_c;
};
static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
struct MemoryPoolInfo {
u64_le pool_address;
u64_le pool_size;
MemoryPoolStates pool_state;
INSERT_PADDING_WORDS(3); // Unknown
};
static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
struct BiquadFilter {
u8 enable;
INSERT_PADDING_BYTES(1);
std::array<s16_le, 3> numerator;
std::array<s16_le, 2> denominator;
};
static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
struct WaveBuffer {
u64_le buffer_addr;
u64_le buffer_sz;
s32_le start_sample_offset;
s32_le end_sample_offset;
u8 is_looping;
u8 end_of_stream;
u8 sent_to_server;
INSERT_PADDING_BYTES(5);
u64 context_addr;
u64 context_sz;
INSERT_PADDING_BYTES(8);
};
static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
struct VoiceInfo {
u32_le id;
u32_le node_id;
u8 is_new;
u8 is_in_use;
PlayState play_state;
u8 sample_format;
u32_le sample_rate;
u32_le priority;
u32_le sorting_order;
u32_le channel_count;
float_le pitch;
float_le volume;
std::array<BiquadFilter, 2> biquad_filter;
u32_le wave_buffer_count;
u32_le wave_buffer_head;
INSERT_PADDING_WORDS(1);
u64_le additional_params_addr;
u64_le additional_params_sz;
u32_le mix_id;
u32_le splitter_info_id;
std::array<WaveBuffer, 4> wave_buffer;
std::array<u32_le, 6> voice_channel_resource_ids;
INSERT_PADDING_BYTES(24);
};
static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
struct VoiceOutStatus {
u64_le played_sample_count;
u32_le wave_buffer_consumed;
u32_le voice_drops_count;
};
static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
struct UpdateDataHeader {
UpdateDataHeader() {}
explicit UpdateDataHeader(const AudioRendererParameter& config) {
revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
behavior_size = 0xb0;
memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
voices_size = config.voice_count * 0x10;
voice_resource_size = 0x0;
effects_size = config.effect_count * 0x10;
mixes_size = 0x0;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
effects_size + sinks_size + performance_manager_size;
}
u32_le revision;
u32_le behavior_size;
u32_le memory_pools_size;
u32_le voices_size;
u32_le voice_resource_size;
u32_le effects_size;
u32_le mixes_size;
u32_le sinks_size;
u32_le performance_manager_size;
INSERT_PADDING_WORDS(6);
u32_le total_size;
};
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
class AudioRenderer {
public:
AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
void QueueMixedBuffer(Buffer::Tag tag);
void ReleaseAndQueueBuffers();
private:
class VoiceState {
public:
bool IsPlaying() const {
return is_in_use && info.play_state == PlayState::Started;
}
const VoiceOutStatus& GetOutStatus() const {
return out_status;
}
const VoiceInfo& GetInfo() const {
return info;
}
VoiceInfo& Info() {
return info;
}
void SetWaveIndex(size_t index);
std::vector<s16> DequeueSamples(size_t sample_count);
void UpdateState();
void RefreshBuffer();
private:
bool is_in_use{};
bool is_refresh_pending{};
size_t wave_index{};
size_t offset{};
Codec::ADPCMState adpcm_state{};
std::vector<s16> samples;
VoiceOutStatus out_status{};
VoiceInfo info{};
};
AudioRendererParameter worker_params;
Kernel::SharedPtr<Kernel::Event> buffer_event;
std::vector<VoiceState> voices;
std::unique_ptr<AudioCore::AudioOut> audio_core;
AudioCore::StreamPtr stream;
};
} // namespace AudioCore

View File

@@ -18,16 +18,11 @@ class Buffer {
public:
using Tag = u64;
Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
/// Returns the raw audio data for the buffer
std::vector<s16>& Samples() {
return samples;
}
/// Returns the raw audio data for the buffer
const std::vector<s16>& GetSamples() const {
return samples;
const std::vector<u8>& GetData() const {
return data;
}
/// Returns the buffer tag, this is provided by the game to the audout service
@@ -37,7 +32,7 @@ public:
private:
Tag tag;
std::vector<s16> samples;
std::vector<u8> data;
};
using BufferPtr = std::shared_ptr<Buffer>;

View File

@@ -1,77 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "audio_core/codec.h"
namespace AudioCore::Codec {
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state) {
// GC-ADPCM with scale factor and variable coefficients.
// Frames are 8 bytes long containing 14 samples each.
// Samples are 4 bits (one nibble) long.
constexpr size_t FRAME_LEN = 8;
constexpr size_t SAMPLES_PER_FRAME = 14;
constexpr std::array<int, 16> SIGNED_NIBBLES = {
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
const size_t ret_size =
sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
std::vector<s16> ret(ret_size);
int yn1 = state.yn1, yn2 = state.yn2;
const size_t NUM_FRAMES =
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
const int frame_header = data[framei * FRAME_LEN];
const int scale = 1 << (frame_header & 0xF);
const int idx = (frame_header >> 4) & 0x7;
// Coefficients are fixed point with 11 bits fractional part.
const int coef1 = coeff[idx * 2 + 0];
const int coef2 = coeff[idx * 2 + 1];
// Decodes an audio sample. One nibble produces one sample.
const auto decode_sample = [&](const int nibble) -> s16 {
const int xn = nibble * scale;
// We first transform everything into 11 bit fixed point, perform the second order
// digital filter, then transform back.
// 0x400 == 0.5 in 11 bit fixed point.
// Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
// Clamp to output range.
val = std::clamp<s32>(val, -32768, 32767);
// Advance output feedback.
yn2 = yn1;
yn1 = val;
return static_cast<s16>(val);
};
size_t outputi = framei * SAMPLES_PER_FRAME;
size_t datai = framei * FRAME_LEN + 1;
for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
ret[outputi] = sample1;
outputi++;
const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
ret[outputi] = sample2;
outputi++;
datai++;
}
}
state.yn1 = yn1;
state.yn2 = yn2;
return ret;
}
} // namespace AudioCore::Codec

View File

@@ -1,44 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
namespace AudioCore::Codec {
enum class PcmFormat : u32 {
Invalid = 0,
Int8 = 1,
Int16 = 2,
Int24 = 3,
Int32 = 4,
PcmFloat = 5,
Adpcm = 6,
};
/// See: Codec::DecodeADPCM
struct ADPCMState {
// Two historical samples from previous processed buffer,
// required for ADPCM decoding
s16 yn1; ///< y[n-1]
s16 yn2; ///< y[n-2]
};
using ADPCM_Coeff = std::array<s16, 16>;
/**
* @param data Pointer to buffer that contains ADPCM data to decode
* @param size Size of buffer in bytes
* @param coeff ADPCM coefficients
* @param state ADPCM state, this is updated with new state
* @return Decoded stereo signed PCM16 data, sample_count in length
*/
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state);
}; // namespace AudioCore::Codec

View File

@@ -13,30 +13,20 @@ namespace AudioCore {
class SinkStreamImpl final : public SinkStream {
public:
SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
const std::string& name)
: ctx{ctx}, num_channels{num_channels_} {
if (num_channels == 6) {
// 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
// channel for now
is_6_channel = true;
num_channels = 2;
}
cubeb_stream_params params{};
params.rate = sample_rate;
params.channels = num_channels;
SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} {
cubeb_stream_params params;
params.rate = 48000;
params.channels = GetNumChannels();
params.format = CUBEB_SAMPLE_S16NE;
params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
params.layout = CUBEB_LAYOUT_STEREO;
u32 minimum_latency{};
u32 minimum_latency = 0;
if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
}
if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
&params, std::max(512u, minimum_latency),
if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr,
output_device, &params, std::max(512u, minimum_latency),
&SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
this) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
@@ -61,29 +51,33 @@ public:
cubeb_stream_destroy(stream_backend);
}
void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override {
if (!ctx) {
return;
}
queue.reserve(queue.size() + samples.size() * GetNumChannels());
queue.reserve(queue.size() + sample_count * GetNumChannels());
if (is_6_channel) {
if (num_channels == 2) {
// Copy as-is
std::copy(samples, samples + sample_count * GetNumChannels(),
std::back_inserter(queue));
} else if (num_channels == 6) {
// Downsample 6 channels to 2
const size_t sample_count_copy_size = samples.size() * 2;
const size_t sample_count_copy_size = sample_count * num_channels * 2;
queue.reserve(sample_count_copy_size);
for (size_t i = 0; i < samples.size(); i += num_channels) {
for (size_t i = 0; i < sample_count * num_channels; i += num_channels) {
queue.push_back(samples[i]);
queue.push_back(samples[i + 1]);
}
} else {
// Copy as-is
std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
ASSERT_MSG(false, "Unimplemented");
}
}
u32 GetNumChannels() const {
return num_channels;
// Only support 2-channel stereo output for now
return 2;
}
private:
@@ -91,8 +85,6 @@ private:
cubeb* ctx{};
cubeb_stream* stream_backend{};
u32 num_channels{};
bool is_6_channel{};
std::vector<s16> queue;
@@ -137,10 +129,8 @@ CubebSink::~CubebSink() {
cubeb_destroy(ctx);
}
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) {
sink_streams.push_back(
std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) {
sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device));
return *sink_streams.back();
}

View File

@@ -18,8 +18,7 @@ public:
explicit CubebSink(std::string device_id);
~CubebSink() override;
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) override;
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override;
private:
cubeb* ctx{};

View File

@@ -13,14 +13,14 @@ public:
explicit NullSink(std::string){};
~NullSink() override = default;
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
const std::string& /*name*/) override {
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override {
return null_sink_stream;
}
private:
struct NullSinkStreamImpl final : SinkStream {
void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/,
size_t /*sample_count*/) override {}
} null_sink_stream;
};

View File

@@ -5,7 +5,6 @@
#pragma once
#include <memory>
#include <string>
#include "audio_core/sink_stream.h"
#include "common/common_types.h"
@@ -22,8 +21,7 @@ constexpr char auto_device_name[] = "auto";
class Sink {
public:
virtual ~Sink() = default;
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) = 0;
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0;
};
using SinkPtr = std::unique_ptr<Sink>;

View File

@@ -5,7 +5,6 @@
#pragma once
#include <memory>
#include <vector>
#include "common/common_types.h"
@@ -23,8 +22,9 @@ public:
* Feed stereo samples to sink.
* @param num_channels Number of channels used.
* @param samples Samples in interleaved stereo PCM16 format.
* @param sample_count Number of samples.
*/
virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0;
};
using SinkStreamPtr = std::unique_ptr<SinkStream>;

View File

@@ -32,13 +32,17 @@ u32 Stream::GetNumChannels() const {
return {};
}
u32 Stream::GetSampleSize() const {
return GetNumChannels() * 2;
}
Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
SinkStream& sink_stream, std::string&& name_)
SinkStream& sink_stream)
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
sink_stream{sink_stream}, name{std::move(name_)} {
sink_stream{sink_stream} {
release_event = CoreTiming::RegisterEvent(
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
"Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
}
void Stream::Play() {
@@ -51,15 +55,17 @@ void Stream::Stop() {
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
const size_t num_samples{buffer.GetData().size() / GetSampleSize()};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
}
static void VolumeAdjustSamples(std::vector<s16>& samples) {
static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
std::vector<s16> samples(data.size() / sizeof(s16));
std::memcpy(samples.data(), data.data(), data.size());
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
if (volume == 1.0f) {
return;
return samples;
}
// Implementation of a volume slider with a dynamic range of 60 dB
@@ -67,6 +73,8 @@ static void VolumeAdjustSamples(std::vector<s16>& samples) {
for (auto& sample : samples) {
sample = static_cast<s16>(sample * volume_scale_factor);
}
return samples;
}
void Stream::PlayNextBuffer() {
@@ -88,14 +96,14 @@ void Stream::PlayNextBuffer() {
active_buffer = queued_buffers.front();
queued_buffers.pop();
VolumeAdjustSamples(active_buffer->Samples());
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()};
sink_stream.EnqueueSamples(
GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count);
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
void Stream::ReleaseActiveBuffer() {
ASSERT(active_buffer);
released_buffers.push(std::move(active_buffer));
release_callback();
PlayNextBuffer();

View File

@@ -6,7 +6,6 @@
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <queue>
@@ -34,7 +33,7 @@ public:
using ReleaseCallback = std::function<void()>;
Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
SinkStream& sink_stream, std::string&& name_);
SinkStream& sink_stream);
/// Plays the audio stream
void Play();
@@ -69,6 +68,9 @@ public:
/// Gets the number of channels
u32 GetNumChannels() const;
/// Gets the sample size in bytes
u32 GetSampleSize() const;
private:
/// Current state of the stream
enum class State {
@@ -94,7 +96,6 @@ private:
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
SinkStream& sink_stream; ///< Output sink for the stream
std::string name; ///< Name of the stream, must be unique
};
using StreamPtr = std::shared_ptr<Stream>;

View File

@@ -8,7 +8,6 @@
#include <cstdio>
#include <fstream>
#include <functional>
#include <limits>
#include <string>
#include <string_view>
#include <type_traits>
@@ -211,9 +210,8 @@ public:
static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
if (!IsOpen()) {
return std::numeric_limits<size_t>::max();
}
if (!IsOpen())
return -1;
return std::fread(data, sizeof(T), length, m_file);
}
@@ -222,10 +220,8 @@ public:
size_t WriteArray(const T* data, size_t length) {
static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
if (!IsOpen()) {
return std::numeric_limits<size_t>::max();
}
if (!IsOpen())
return -1;
return std::fwrite(data, sizeof(T), length, m_file);
}

View File

@@ -168,7 +168,6 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, ARP) \
SUB(Service, BCAT) \
SUB(Service, BPC) \
SUB(Service, BTM) \
@@ -200,7 +199,6 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, SPL) \
SUB(Service, SSL) \
SUB(Service, Time) \
SUB(Service, USB) \
SUB(Service, VI) \
SUB(Service, WLAN) \
CLS(HW) \

View File

@@ -54,7 +54,6 @@ enum class Class : ClassType {
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
Service_ARP, ///< The ARP service
Service_Audio, ///< The Audio (Audio control) service
Service_BCAT, ///< The BCAT service
Service_BPC, ///< The BPC service
@@ -87,7 +86,6 @@ enum class Class : ClassType {
Service_SPL, ///< The SPL service
Service_SSL, ///< The SSL service
Service_Time, ///< The time service
Service_USB, ///< The USB (Universal Serial Bus) service
Service_VI, ///< The VI (Video interface) service
Service_WLAN, ///< The WLAN (Wireless local area network) service
HW, ///< Low-level hardware emulation

View File

@@ -134,8 +134,6 @@ add_library(core STATIC
hle/service/apm/apm.h
hle/service/apm/interface.cpp
hle/service/apm/interface.h
hle/service/arp/arp.cpp
hle/service/arp/arp.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
hle/service/audio/auddbg.cpp
@@ -315,8 +313,6 @@ add_library(core STATIC
hle/service/time/interface.h
hle/service/time/time.cpp
hle/service/time/time.h
hle/service/usb/usb.cpp
hle/service/usb/usb.h
hle/service/vi/vi.cpp
hle/service/vi/vi.h
hle/service/vi/vi_m.cpp

View File

@@ -203,7 +203,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->context);
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
if (last_bkpt_hit || (num_instructions == 1)) {
last_bkpt_hit = false;
GDBStub::Break();
GDBStub::SendTrap(thread, 5);

View File

@@ -18,7 +18,6 @@
#include "core/loader/loader.h"
#include "core/settings.h"
#include "file_sys/vfs_real.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Core {
@@ -62,6 +61,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
GDBStub::SetCpuStepFlag(false);
tight_loop = false;
} else {
return ResultStatus::Success;
@@ -77,10 +77,6 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
@@ -182,6 +178,7 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
gpu_core = std::make_unique<Tegra::GPU>();
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -189,13 +186,10 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
Service::Init(service_manager);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
if (!VideoCore::Init(emu_window)) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
@@ -227,7 +221,7 @@ void System::Shutdown() {
perf_results.frametime * 1000.0);
// Shutdown emulation session
renderer.reset();
VideoCore::Shutdown();
GDBStub::Shutdown();
Service::Shutdown();
Kernel::Shutdown();

View File

@@ -27,10 +27,6 @@ namespace Service::SM {
class ServiceManager;
}
namespace VideoCore {
class RendererBase;
}
namespace Core {
class System {
@@ -82,17 +78,6 @@ public:
*/
ResultStatus SingleStep();
/**
* Invalidate the CPU instruction caches
* This function should only be used by GDB Stub to support breakpoints, memory updates and
* step/continue commands.
*/
void InvalidateCpuInstructionCaches() {
for (auto& cpu : cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
/// Shutdown the emulated system.
void Shutdown();
@@ -144,26 +129,11 @@ public:
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(size_t core_index);
/// Gets a mutable reference to the GPU interface
/// Gets the GPU interface
Tegra::GPU& GPU() {
return *gpu_core;
}
/// Gets an immutable reference to the GPU interface.
const Tegra::GPU& GPU() const {
return *gpu_core;
}
/// Gets a mutable reference to the renderer.
VideoCore::RendererBase& Renderer() {
return *renderer;
}
/// Gets an immutable reference to the renderer.
const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
@@ -227,7 +197,6 @@ private:
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;

View File

@@ -141,7 +141,7 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user
ForceExceptionCheck(cycles_into_future);
event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
}
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
@@ -156,7 +156,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
}
}
@@ -167,7 +167,7 @@ void RemoveEvent(const EventType* event_type) {
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
}
}
@@ -190,7 +190,7 @@ void MoveEvents() {
for (Event ev; ts_queue.Pop(ev);) {
ev.fifo_order = event_fifo_id++;
event_queue.emplace_back(std::move(ev));
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
}
}
@@ -205,7 +205,7 @@ void Advance() {
while (!event_queue.empty() && event_queue.front().time <= global_timer) {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
event_queue.pop_back();
evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time));
}
@@ -226,8 +226,8 @@ void Idle() {
downcount = 0;
}
std::chrono::microseconds GetGlobalTimeUs() {
return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
u64 GetGlobalTimeUs() {
return GetTicks() * 1000000 / BASE_CLOCK_RATE;
}
int GetDowncount() {

View File

@@ -17,17 +17,12 @@
* ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
*/
#include <chrono>
#include <functional>
#include <string>
#include "common/common_types.h"
namespace CoreTiming {
struct EventType;
using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
/**
* CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
* required to end slice -1 and start slice 0 before the first cycle of code is executed.
@@ -35,6 +30,8 @@ using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
void Init();
void Shutdown();
typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
/**
* This should only be called from the emu thread, if you are calling it any other thread, you are
* doing something evil
@@ -43,6 +40,8 @@ u64 GetTicks();
u64 GetIdleTicks();
void AddTicks(u64 ticks);
struct EventType;
/**
* Returns the event_type identifier. if name is not unique, it will assert.
*/
@@ -87,7 +86,7 @@ void ClearPendingEvents();
void ForceExceptionCheck(s64 cycles);
std::chrono::microseconds GetGlobalTimeUs();
u64 GetGlobalTimeUs();
int GetDowncount();

View File

@@ -3,22 +3,10 @@
// Refer to the license.txt file included.
#include <mbedtls/cipher.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
namespace Core::Crypto {
namespace {
std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
std::vector<u8> out(0x10);
for (size_t i = 0xF; i <= 0xF; --i) {
out[i] = sector_id & 0xFF;
sector_id >>= 8;
}
return out;
}
} // Anonymous namespace
static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR),
"CTR has incorrect value.");
@@ -68,28 +56,27 @@ void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
}
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const {
auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) {
size_t written = 0;
const auto context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
mbedtls_cipher_reset(context);
size_t written = 0;
if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
mbedtls_cipher_update(context, src, size, dest, &written);
if (written != size) {
if (written != size)
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
size, written);
}
} else {
const auto block_size = mbedtls_cipher_get_block_size(context);
for (size_t offset = 0; offset < size; offset += block_size) {
auto length = std::min<size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
if (written != length) {
if (written != length)
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
length, written);
}
}
}
@@ -110,6 +97,16 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest,
}
}
template <typename Key, size_t KeySize>
std::vector<u8> AESCipher<Key, KeySize>::CalculateNintendoTweak(size_t sector_id) {
std::vector<u8> out(0x10);
for (size_t i = 0xF; i <= 0xF; --i) {
out[i] = sector_id & 0xFF;
sector_id >>= 8;
}
return out;
}
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // namespace Core::Crypto

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
#include "common/assert.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {
@@ -38,19 +38,15 @@ public:
void SetIV(std::vector<u8> iv);
template <typename Source, typename Dest>
void Transcode(const Source* src, size_t size, Dest* dest, Op op) const {
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
"Transcode source and destination types must be trivially copyable.");
void Transcode(const Source* src, size_t size, Dest* dest, Op op) {
Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
}
void Transcode(const u8* src, size_t size, u8* dest, Op op) const;
void Transcode(const u8* src, size_t size, u8* dest, Op op);
template <typename Source, typename Dest>
void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id,
size_t sector_size, Op op) {
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
"XTSTranscode source and destination types must be trivially copyable.");
XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id,
sector_size, op);
}
@@ -60,5 +56,7 @@ public:
private:
std::unique_ptr<CipherContext> ctx;
static std::vector<u8> CalculateNintendoTweak(size_t sector_id);
};
} // namespace Core::Crypto

View File

@@ -4,7 +4,6 @@
#pragma once
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {

View File

@@ -2,16 +2,19 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <fstream>
#include <locale>
#include <sstream>
#include <string_view>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/crypto/key_manager.h"
#include "core/settings.h"
#include "key_manager.h"
namespace Core::Crypto {
@@ -63,7 +66,8 @@ KeyManager::KeyManager() {
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) {
const auto filename = std::string(filename_);
std::ifstream file(filename);
if (!file.is_open())
return;
@@ -103,8 +107,11 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
}
}
void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title) {
void KeyManager::AttemptLoadKeyFile(std::string_view dir1_, std::string_view dir2_,
std::string_view filename_, bool title) {
const std::string dir1(dir1_);
const std::string dir2(dir2_);
const std::string filename(filename_);
if (FileUtil::Exists(dir1 + DIR_SEP + filename))
LoadFromFile(dir1 + DIR_SEP + filename, title);
else if (FileUtil::Exists(dir2 + DIR_SEP + filename))

View File

@@ -5,7 +5,6 @@
#pragma once
#include <array>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
@@ -110,9 +109,9 @@ private:
std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
bool dev_mode;
void LoadFromFile(const std::string& filename, bool is_title_keys);
void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title);
void LoadFromFile(std::string_view filename, bool is_title_keys);
void AttemptLoadKeyFile(std::string_view dir1, std::string_view dir2, std::string_view filename,
bool title);
static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id;

View File

@@ -41,42 +41,40 @@
#include "core/loader/loader.h"
#include "core/memory.h"
namespace GDBStub {
namespace {
constexpr int GDB_BUFFER_SIZE = 10000;
const int GDB_BUFFER_SIZE = 10000;
constexpr char GDB_STUB_START = '$';
constexpr char GDB_STUB_END = '#';
constexpr char GDB_STUB_ACK = '+';
constexpr char GDB_STUB_NACK = '-';
const char GDB_STUB_START = '$';
const char GDB_STUB_END = '#';
const char GDB_STUB_ACK = '+';
const char GDB_STUB_NACK = '-';
#ifndef SIGTRAP
constexpr u32 SIGTRAP = 5;
const u32 SIGTRAP = 5;
#endif
#ifndef SIGTERM
constexpr u32 SIGTERM = 15;
const u32 SIGTERM = 15;
#endif
#ifndef MSG_WAITALL
constexpr u32 MSG_WAITALL = 8;
const u32 MSG_WAITALL = 8;
#endif
constexpr u32 LR_REGISTER = 30;
constexpr u32 SP_REGISTER = 31;
constexpr u32 PC_REGISTER = 32;
constexpr u32 CPSR_REGISTER = 33;
constexpr u32 UC_ARM64_REG_Q0 = 34;
constexpr u32 FPSCR_REGISTER = 66;
const u32 LR_REGISTER = 30;
const u32 SP_REGISTER = 31;
const u32 PC_REGISTER = 32;
const u32 CPSR_REGISTER = 33;
const u32 UC_ARM64_REG_Q0 = 34;
const u32 FPSCR_REGISTER = 66;
// TODO/WiP - Used while working on support for FPU
constexpr u32 TODO_DUMMY_REG_997 = 997;
constexpr u32 TODO_DUMMY_REG_998 = 998;
const u32 TODO_DUMMY_REG_997 = 997;
const u32 TODO_DUMMY_REG_998 = 998;
// For sample XML files see the GDB source /gdb/features
// GDB also wants the l character at the start
// This XML defines what the registers are for this specific ARM device
constexpr char target_xml[] =
static const char* target_xml =
R"(l<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
@@ -142,28 +140,30 @@ constexpr char target_xml[] =
</target>
)";
int gdbserver_socket = -1;
namespace GDBStub {
u8 command_buffer[GDB_BUFFER_SIZE];
u32 command_length;
static int gdbserver_socket = -1;
u32 latest_signal = 0;
bool memory_break = false;
static u8 command_buffer[GDB_BUFFER_SIZE];
static u32 command_length;
Kernel::Thread* current_thread = nullptr;
u32 current_core = 0;
static u32 latest_signal = 0;
static bool memory_break = false;
static Kernel::Thread* current_thread = nullptr;
static u32 current_core = 0;
// Binding to a port within the reserved ports range (0-1023) requires root permissions,
// so default to a port outside of that range.
u16 gdbstub_port = 24689;
static u16 gdbstub_port = 24689;
bool halt_loop = true;
bool step_loop = false;
bool send_trap = false;
static bool halt_loop = true;
static bool step_loop = false;
static bool send_trap = false;
// If set to false, the server will never be started and no
// gdbstub-related functions will be executed.
std::atomic<bool> server_enabled(false);
static std::atomic<bool> server_enabled(false);
#ifdef _WIN32
WSADATA InitData;
@@ -171,26 +171,23 @@ WSADATA InitData;
struct Breakpoint {
bool active;
VAddr addr;
PAddr addr;
u64 len;
std::array<u8, 4> inst;
};
using BreakpointMap = std::map<VAddr, Breakpoint>;
BreakpointMap breakpoints_execute;
BreakpointMap breakpoints_read;
BreakpointMap breakpoints_write;
static std::map<u64, Breakpoint> breakpoints_execute;
static std::map<u64, Breakpoint> breakpoints_read;
static std::map<u64, Breakpoint> breakpoints_write;
struct Module {
std::string name;
VAddr beg;
VAddr end;
PAddr beg;
PAddr end;
};
std::vector<Module> modules;
} // Anonymous namespace
static std::vector<Module> modules;
void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) {
Module module;
if (add_elf_ext) {
Common::SplitPath(name, nullptr, &module.name, nullptr);
@@ -421,11 +418,11 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) {
}
/**
* Get the map of breakpoints for a given breakpoint type.
* Get the list of breakpoints for a given breakpoint type.
*
* @param type Type of breakpoint map.
* @param type Type of breakpoint list.
*/
static BreakpointMap& GetBreakpointMap(BreakpointType type) {
static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
switch (type) {
case BreakpointType::Execute:
return breakpoints_execute;
@@ -444,24 +441,20 @@ static BreakpointMap& GetBreakpointMap(BreakpointType type) {
* @param type Type of breakpoint.
* @param addr Address of breakpoint.
*/
static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
BreakpointMap& p = GetBreakpointMap(type);
static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
const auto bp = p.find(addr);
if (bp == p.end()) {
return;
auto bp = p.find(static_cast<u64>(addr));
if (bp != p.end()) {
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
bp->second.len, bp->second.addr, static_cast<int>(type));
p.erase(static_cast<u64>(addr));
}
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
bp->second.len, bp->second.addr, static_cast<int>(type));
Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
p.erase(addr);
}
BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) {
const BreakpointMap& p = GetBreakpointMap(type);
const auto next_breakpoint = p.lower_bound(addr);
BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) {
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
auto next_breakpoint = p.lower_bound(static_cast<u64>(addr));
BreakpointAddress breakpoint;
if (next_breakpoint != p.end()) {
@@ -475,38 +468,36 @@ BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type)
return breakpoint;
}
bool CheckBreakpoint(VAddr addr, BreakpointType type) {
bool CheckBreakpoint(PAddr addr, BreakpointType type) {
if (!IsConnected()) {
return false;
}
const BreakpointMap& p = GetBreakpointMap(type);
const auto bp = p.find(addr);
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
if (bp == p.end()) {
return false;
}
auto bp = p.find(static_cast<u64>(addr));
if (bp != p.end()) {
u64 len = bp->second.len;
u64 len = bp->second.len;
// IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
// no matter if it's a 4-byte or 2-byte instruction. When you execute a
// Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
// two instructions instead of the single instruction you placed the breakpoint
// on. So, as a way to make sure that execution breakpoints are only breaking
// on the instruction that was specified, set the length of an execution
// breakpoint to 1. This should be fine since the CPU should never begin executing
// an instruction anywhere except the beginning of the instruction.
if (type == BreakpointType::Execute) {
len = 1;
}
// IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
// no matter if it's a 4-byte or 2-byte instruction. When you execute a
// Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
// two instructions instead of the single instruction you placed the breakpoint
// on. So, as a way to make sure that execution breakpoints are only breaking
// on the instruction that was specified, set the length of an execution
// breakpoint to 1. This should be fine since the CPU should never begin executing
// an instruction anywhere except the beginning of the instruction.
if (type == BreakpointType::Execute) {
len = 1;
}
if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
LOG_DEBUG(Debug_GDBStub,
"Found breakpoint type {} @ {:016X}, range: {:016X}"
" - {:016X} ({:X} bytes)",
static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
return true;
if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
LOG_DEBUG(Debug_GDBStub,
"Found breakpoint type {} @ {:016X}, range: {:016X}"
" - {:016X} ({:X} bytes)",
static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
return true;
}
}
return false;
@@ -940,7 +931,6 @@ static void WriteMemory() {
GdbHexToMem(data.data(), len_pos + 1, len);
Memory::WriteBlock(addr, data.data(), len);
Core::System::GetInstance().InvalidateCpuInstructionCaches();
SendReply("OK");
}
@@ -960,7 +950,6 @@ static void Step() {
step_loop = true;
halt_loop = true;
send_trap = true;
Core::System::GetInstance().InvalidateCpuInstructionCaches();
}
/// Tell the CPU if we hit a memory breakpoint.
@@ -977,7 +966,6 @@ static void Continue() {
memory_break = false;
step_loop = false;
halt_loop = false;
Core::System::GetInstance().InvalidateCpuInstructionCaches();
}
/**
@@ -987,17 +975,13 @@ static void Continue() {
* @param addr Address of breakpoint.
* @param len Length of breakpoint.
*/
static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
BreakpointMap& p = GetBreakpointMap(type);
static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) {
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
Breakpoint breakpoint;
breakpoint.active = true;
breakpoint.addr = addr;
breakpoint.len = len;
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}};
Memory::WriteBlock(addr, btrap.data(), btrap.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
p.insert({addr, breakpoint});
LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
@@ -1031,7 +1015,7 @@ static void AddBreakpoint() {
auto start_offset = command_buffer + 3;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1;
u64 len =
@@ -1080,7 +1064,7 @@ static void RemoveBreakpoint() {
auto start_offset = command_buffer + 3;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
if (type == BreakpointType::Access) {
// Access is made up of Read and Write types, so add both breakpoints

View File

@@ -22,7 +22,7 @@ enum class BreakpointType {
};
struct BreakpointAddress {
VAddr address;
PAddr address;
BreakpointType type;
};
@@ -53,7 +53,7 @@ bool IsServerEnabled();
bool IsConnected();
/// Register module.
void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true);
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true);
/**
* Signal to the gdbstub server that it should halt CPU execution.
@@ -74,7 +74,7 @@ void HandlePacket();
* @param addr Address to search from.
* @param type Type of breakpoint.
*/
BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type);
BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type);
/**
* Check if a breakpoint of the specified type exists at the given address.
@@ -82,7 +82,7 @@ BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointTy
* @param addr Address of breakpoint.
* @param type Type of breakpoint.
*/
bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type);
bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type);
/// If set to true, the CPU will halt at the beginning of the next CPU loop.
bool GetCpuHaltFlag();

View File

@@ -32,8 +32,9 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) {
}
// Gets the threads waiting on an address.
static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
const auto RetrieveWaitingThreads =
static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr address) {
auto RetrieveWaitingThreads =
[](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
auto& thread_list = scheduler->GetThreadList();
@@ -44,20 +45,16 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
}
};
// Retrieve all threads that are waiting for this address.
std::vector<SharedPtr<Thread>> threads;
RetrieveWaitingThreads(0, threads, address);
RetrieveWaitingThreads(1, threads, address);
RetrieveWaitingThreads(2, threads, address);
RetrieveWaitingThreads(3, threads, address);
// Retrieve a list of all threads that are waiting for this address.
RetrieveWaitingThreads(0, waiting_threads, address);
RetrieveWaitingThreads(1, waiting_threads, address);
RetrieveWaitingThreads(2, waiting_threads, address);
RetrieveWaitingThreads(3, waiting_threads, address);
// Sort them by priority, such that the highest priority ones come first.
std::sort(threads.begin(), threads.end(),
std::sort(waiting_threads.begin(), waiting_threads.end(),
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
return lhs->current_priority < rhs->current_priority;
});
return threads;
}
// Wake up num_to_wake (or all) threads in a vector.
@@ -79,7 +76,9 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
// Signals an address being waited on.
ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
// Get threads waiting on the address.
std::vector<SharedPtr<Thread>> waiting_threads;
GetThreadsWaitingOnAddress(waiting_threads, address);
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
@@ -111,11 +110,12 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
}
// Get threads waiting on the address.
std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
std::vector<SharedPtr<Thread>> waiting_threads;
GetThreadsWaitingOnAddress(waiting_threads, address);
// Determine the modified value depending on the waiting count.
s32 updated_value;
if (waiting_threads.empty()) {
if (waiting_threads.size() == 0) {
updated_value = value - 1;
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value + 1;

View File

@@ -14,8 +14,8 @@
namespace Kernel {
ClientPort::ClientPort() = default;
ClientPort::~ClientPort() = default;
ClientPort::ClientPort() {}
ClientPort::~ClientPort() {}
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
// Note: Threads do not wait for the server endpoint to call
@@ -40,12 +40,4 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
}
void ClientPort::ConnectionClosed() {
if (active_sessions == 0) {
return;
}
--active_sessions;
}
} // namespace Kernel

View File

@@ -37,20 +37,14 @@ public:
*/
ResultVal<SharedPtr<ClientSession>> Connect();
/**
* Signifies that a previously active connection has been closed,
* decreasing the total number of active connections to this port.
*/
void ConnectionClosed();
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have
u32 active_sessions; ///< Number of currently open sessions to this port
std::string name; ///< Name of client port (optional)
private:
ClientPort();
~ClientPort() override;
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
u32 active_sessions = 0; ///< Number of currently open sessions to this port
std::string name; ///< Name of client port (optional)
};
} // namespace Kernel

View File

@@ -31,9 +31,10 @@ public:
return HANDLE_TYPE;
}
ResetType GetResetType() const {
return reset_type;
}
ResetType reset_type; ///< Current ResetType
bool signaled; ///< Whether the event has already been signaled
std::string name; ///< Name of event (optional)
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
@@ -46,11 +47,6 @@ public:
private:
Event();
~Event() override;
ResetType reset_type; ///< Current ResetType
bool signaled; ///< Whether the event has already been signaled
std::string name; ///< Name of event (optional)
};
} // namespace Kernel

View File

@@ -27,7 +27,7 @@ ServerSession::~ServerSession() {
// Decrease the port's connection count.
if (parent->port)
parent->port->ConnectionClosed();
parent->port->active_sessions--;
// TODO(Subv): Wake up all the ClientSession's waiting threads and set
// the SendSyncRequest result to 0xC920181A.

View File

@@ -1,75 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/arp/arp.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::ARP {
class ARP_R final : public ServiceFramework<ARP_R> {
public:
explicit ARP_R() : ServiceFramework{"arp:r"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetApplicationLaunchProperty"},
{1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"},
{2, nullptr, "GetApplicationControlProperty"},
{3, nullptr, "GetApplicationControlPropertyWithApplicationId"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class IRegistrar final : public ServiceFramework<IRegistrar> {
public:
explicit IRegistrar() : ServiceFramework{"IRegistrar"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Issue"},
{1, nullptr, "SetApplicationLaunchProperty"},
{2, nullptr, "SetApplicationControlProperty"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class ARP_W final : public ServiceFramework<ARP_W> {
public:
explicit ARP_W() : ServiceFramework{"arp:w"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
{1, nullptr, "DeleteProperties"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void AcquireRegistrar(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IRegistrar>();
LOG_DEBUG(Service_ARP, "called");
}
};
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<ARP_R>()->InstallAsService(sm);
std::make_shared<ARP_W>()->InstallAsService(sm);
}
} // namespace Service::ARP

View File

@@ -1,16 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Service::SM {
class ServiceManager;
}
namespace Service::ARP {
/// Registers all ARP services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& sm);
} // namespace Service::ARP

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audin_a.h"
namespace Service::Audio {

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audout_a.h"
namespace Service::Audio {

View File

@@ -4,8 +4,6 @@
#include <array>
#include <vector>
#include "audio_core/codec.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
@@ -50,7 +48,7 @@ public:
buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
"IAudioOut", [=]() { buffer_event->Signal(); });
[=]() { buffer_event->Signal(); });
}
private:
@@ -113,10 +111,10 @@ private:
std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
const u64 tag{rp.Pop<u64>()};
std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
std::vector<u8> data(audio_buffer.buffer_size);
Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size());
if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
if (!audio_core.QueueBuffer(stream, tag, std::move(data))) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
}
@@ -202,7 +200,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(DefaultSampleRate);
rb.Push<u32>(params.channel_count);
rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
}

View File

@@ -38,6 +38,16 @@ private:
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
enum class PcmFormat : u32 {
Invalid = 0,
Int8 = 1,
Int16 = 2,
Int24 = 3,
Int32 = 4,
PcmFloat = 5,
Adpcm = 6,
};
};
} // namespace Service::Audio

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audrec_a.h"
namespace Service::Audio {

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audren_a.h"
namespace Service::Audio {

View File

@@ -15,10 +15,13 @@
namespace Service::Audio {
/// TODO(bunnei): Find a proper value for the audio_ticks
constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
: ServiceFramework("IAudioRenderer") {
explicit IAudioRenderer(AudioRendererParameter audren_params)
: ServiceFramework("IAudioRenderer"), worker_params(audren_params) {
static const FunctionInfo functions[] = {
{0, nullptr, "GetAudioRendererSampleRate"},
{1, nullptr, "GetAudioRendererSampleCount"},
@@ -36,8 +39,21 @@ public:
RegisterHandlers(functions);
system_event =
Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent");
// Register event callback to update the Audio Buffer
audio_event = CoreTiming::RegisterEvent(
"IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
UpdateAudioCallback();
CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
});
// Start the audio event
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
voice_status_list.resize(worker_params.voice_count);
}
~IAudioRenderer() {
CoreTiming::UnscheduleEvent(audio_event, 0);
}
private:
@@ -46,9 +62,61 @@ private:
}
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
UpdateDataHeader config{};
auto buf = ctx.ReadBuffer();
std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader));
u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
std::memcpy(mem_pool_info.data(),
buf.data() + sizeof(UpdateDataHeader) + config.behavior_size,
memory_pool_count * sizeof(MemoryPoolInfo));
std::vector<VoiceInfo> voice_infos(worker_params.voice_count);
std::memcpy(voice_infos.data(),
buf.data() + sizeof(UpdateDataHeader) + config.behavior_size +
config.memory_pools_size + config.voice_resource_size,
worker_params.voice_count * sizeof(VoiceInfo));
UpdateDataHeader response_data{worker_params};
ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
std::vector<u8> output(response_data.total_size);
std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader));
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
for (unsigned i = 0; i < memory_pool.size(); i++) {
if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach)
memory_pool[i].state = MemoryPoolStates::Attached;
else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
memory_pool[i].state = MemoryPoolStates::Detached;
}
std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
response_data.memory_pools_size);
for (unsigned i = 0; i < voice_infos.size(); i++) {
auto& voice_info{voice_infos[i]};
if (voice_info.is_in_use == 0) {
continue;
}
if (voice_info.is_new) {
voice_status_list[i].played_sample_count = 0;
voice_status_list[i].wave_buffer_consumed = 0;
} else if (voice_info.play_state == PlayState::Started) {
// TODO(bunnei): Properly update wave_buffer state here once playback has started.
// Doing this inaccurately breaks games.
}
}
std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size,
voice_status_list.data(), response_data.voices_size);
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
@@ -69,6 +137,8 @@ private:
}
void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
// system_event->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(system_event);
@@ -76,8 +146,131 @@ private:
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
enum class MemoryPoolStates : u32 { // Should be LE
Invalid = 0x0,
Unknown = 0x1,
RequestDetach = 0x2,
Detached = 0x3,
RequestAttach = 0x4,
Attached = 0x5,
Released = 0x6,
};
enum class PlayState : u8 {
Started = 0,
Stopped = 1,
};
struct MemoryPoolEntry {
MemoryPoolStates state;
u32_le unknown_4;
u32_le unknown_8;
u32_le unknown_c;
};
static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
struct MemoryPoolInfo {
u64_le pool_address;
u64_le pool_size;
MemoryPoolStates pool_state;
INSERT_PADDING_WORDS(3); // Unknown
};
static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
struct UpdateDataHeader {
UpdateDataHeader() {}
explicit UpdateDataHeader(const AudioRendererParameter& config) {
revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
behavior_size = 0xb0;
memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
voices_size = config.voice_count * 0x10;
voice_resource_size = 0x0;
effects_size = config.effect_count * 0x10;
mixes_size = 0x0;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
voices_size + effects_size + sinks_size + performance_manager_size;
}
u32_le revision;
u32_le behavior_size;
u32_le memory_pools_size;
u32_le voices_size;
u32_le voice_resource_size;
u32_le effects_size;
u32_le mixes_size;
u32_le sinks_size;
u32_le performance_manager_size;
INSERT_PADDING_WORDS(6);
u32_le total_size;
};
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
struct BiquadFilter {
u8 enable;
INSERT_PADDING_BYTES(1);
s16_le numerator[3];
s16_le denominator[2];
};
static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
struct WaveBuffer {
u64_le buffer_addr;
u64_le buffer_sz;
s32_le start_sample_offset;
s32_le end_sample_offset;
u8 loop;
u8 end_of_stream;
u8 sent_to_server;
INSERT_PADDING_BYTES(5);
u64 context_addr;
u64 context_sz;
INSERT_PADDING_BYTES(8);
};
static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
struct VoiceInfo {
u32_le id;
u32_le node_id;
u8 is_new;
u8 is_in_use;
PlayState play_state;
u8 sample_format;
u32_le sample_rate;
u32_le priority;
u32_le sorting_order;
u32_le channel_count;
float_le pitch;
float_le volume;
BiquadFilter biquad_filter[2];
u32_le wave_buffer_count;
u16_le wave_buffer_head;
INSERT_PADDING_BYTES(6);
u64_le additional_params_addr;
u64_le additional_params_sz;
u32_le mix_id;
u32_le splitter_info_id;
WaveBuffer wave_buffer[4];
u32_le voice_channel_resource_ids[6];
INSERT_PADDING_BYTES(24);
};
static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
struct VoiceOutStatus {
u64_le played_sample_count;
u32_le wave_buffer_consumed;
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
/// This is used to trigger the audio event callback.
CoreTiming::EventType* audio_event;
Kernel::SharedPtr<Kernel::Event> system_event;
std::unique_ptr<AudioCore::AudioRenderer> renderer;
AudioRendererParameter worker_params;
std::vector<VoiceOutStatus> voice_status_list;
};
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
@@ -176,7 +369,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
auto params = rp.PopRaw<AudioRendererParameter>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -187,7 +380,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
auto params = rp.PopRaw<AudioRendererParameter>();
u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
buffer_sz += params.unknown_c * 1024;

View File

@@ -4,7 +4,6 @@
#pragma once
#include "audio_core/audio_renderer.h"
#include "core/hle/service/service.h"
namespace Kernel {
@@ -13,6 +12,24 @@ class HLERequestContext;
namespace Service::Audio {
struct AudioRendererParameter {
u32_le sample_rate;
u32_le sample_count;
u32_le unknown_8;
u32_le unknown_c;
u32_le voice_count;
u32_le sink_count;
u32_le effect_count;
u32_le unknown_1c;
u8 unknown_20;
INSERT_PADDING_BYTES(3);
u32_le splitter_count;
u32_le unknown_2c;
INSERT_PADDING_WORDS(1);
u32_le revision;
};
static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
class AudRenU final : public ServiceFramework<AudRenU> {
public:
explicit AudRenU();

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/filesystem/fsp_ldr.h"
#include "core/hle/service/service.h"

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/filesystem/fsp_pr.h"
#include "core/hle/service/service.h"

View File

@@ -30,9 +30,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
transform, crop_rect};
auto& instance = Core::System::GetInstance();
instance.perf_stats.EndGameFrame();
instance.Renderer().SwapBuffers(framebuffer);
Core::System::GetInstance().perf_stats.EndGameFrame();
VideoCore::g_renderer->SwapBuffers(framebuffer);
}
} // namespace Service::Nvidia::Devices

View File

@@ -150,16 +150,15 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
const auto itr = buffer_mappings.find(params.offset);
auto& gpu = Core::System::GetInstance().GPU();
auto itr = buffer_mappings.find(params.offset);
ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
auto& system_instance = Core::System::GetInstance();
// Remove this memory region from the rasterizer cache.
system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
itr->second.size);
VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size);
auto& gpu = system_instance.GPU();
params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
buffer_mappings.erase(itr->second.offset);

View File

@@ -127,11 +127,9 @@ void NVFlinger::Compose() {
MicroProfileFlip();
if (buffer == boost::none) {
auto& system_instance = Core::System::GetInstance();
// There was no queued buffer to draw, render previous frame
system_instance.perf_stats.EndGameFrame();
system_instance.Renderer().SwapBuffers({});
Core::System::GetInstance().perf_stats.EndGameFrame();
VideoCore::g_renderer->SwapBuffers({});
continue;
}

View File

@@ -19,7 +19,6 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/arp/arp.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bpc/bpc.h"
@@ -63,7 +62,6 @@
#include "core/hle/service/spl/module.h"
#include "core/hle/service/ssl/ssl.h"
#include "core/hle/service/time/time.h"
#include "core/hle/service/usb/usb.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/wlan/wlan.h"
@@ -209,7 +207,6 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
AM::InstallInterfaces(*sm, nv_flinger);
AOC::InstallInterfaces(*sm);
APM::InstallInterfaces(*sm);
ARP::InstallInterfaces(*sm);
Audio::InstallInterfaces(*sm);
BCAT::InstallInterfaces(*sm);
BPC::InstallInterfaces(*sm);
@@ -250,7 +247,6 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
SPL::InstallInterfaces(*sm);
SSL::InstallInterfaces(*sm);
Time::InstallInterfaces(*sm);
USB::InstallInterfaces(*sm);
VI::InstallInterfaces(*sm, nv_flinger);
WLAN::InstallInterfaces(*sm);

View File

@@ -1,238 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/usb/usb.h"
namespace Service::USB {
class IDsInterface final : public ServiceFramework<IDsInterface> {
public:
explicit IDsInterface() : ServiceFramework{"IDsInterface"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetDsEndpoint"},
{1, nullptr, "GetSetupEvent"},
{2, nullptr, "Unknown"},
{3, nullptr, "EnableInterface"},
{4, nullptr, "DisableInterface"},
{5, nullptr, "CtrlInPostBufferAsync"},
{6, nullptr, "CtrlOutPostBufferAsync"},
{7, nullptr, "GetCtrlInCompletionEvent"},
{8, nullptr, "GetCtrlInReportData"},
{9, nullptr, "GetCtrlOutCompletionEvent"},
{10, nullptr, "GetCtrlOutReportData"},
{11, nullptr, "StallCtrl"},
{12, nullptr, "AppendConfigurationData"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class USB_DS final : public ServiceFramework<USB_DS> {
public:
explicit USB_DS() : ServiceFramework{"usb:ds"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "BindDevice"},
{1, nullptr, "BindClientProcess"},
{2, nullptr, "GetDsInterface"},
{3, nullptr, "GetStateChangeEvent"},
{4, nullptr, "GetState"},
{5, nullptr, "ClearDeviceData"},
{6, nullptr, "AddUsbStringDescriptor"},
{7, nullptr, "DeleteUsbStringDescriptor"},
{8, nullptr, "SetUsbDeviceDescriptor"},
{9, nullptr, "SetBinaryObjectStore"},
{10, nullptr, "Enable"},
{11, nullptr, "Disable"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class IClientEpSession final : public ServiceFramework<IClientEpSession> {
public:
explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
{1, nullptr, "Unknown2"},
{2, nullptr, "Unknown3"},
{3, nullptr, "Unknown4"},
{4, nullptr, "PostBufferAsync"},
{5, nullptr, "Unknown5"},
{6, nullptr, "Unknown6"},
{7, nullptr, "Unknown7"},
{8, nullptr, "Unknown8"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class IClientIfSession final : public ServiceFramework<IClientIfSession> {
public:
explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
{1, nullptr, "Unknown2"},
{2, nullptr, "Unknown3"},
{3, nullptr, "Unknown4"},
{4, nullptr, "Unknown5"},
{5, nullptr, "CtrlXferAsync"},
{6, nullptr, "Unknown6"},
{7, nullptr, "GetCtrlXferReport"},
{8, nullptr, "Unknown7"},
{9, nullptr, "GetClientEpSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class USB_HS final : public ServiceFramework<USB_HS> {
public:
explicit USB_HS() : ServiceFramework{"usb:hs"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "BindClientProcess"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
{6, nullptr, "GetInterfaceStateChangeEvent"},
{7, nullptr, "GetClientIfSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class IPdSession final : public ServiceFramework<IPdSession> {
public:
explicit IPdSession() : ServiceFramework{"IPdSession"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "BindNoticeEvent"},
{1, nullptr, "Unknown1"},
{2, nullptr, "GetStatus"},
{3, nullptr, "GetNotice"},
{4, nullptr, "Unknown2"},
{5, nullptr, "Unknown3"},
{6, nullptr, "ReplyPowerRequest"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class USB_PD final : public ServiceFramework<USB_PD> {
public:
explicit USB_PD() : ServiceFramework{"usb:pd"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &USB_PD::GetPdSession, "GetPdSession"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetPdSession(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IPdSession>();
LOG_DEBUG(Service_USB, "called");
}
};
class IPdCradleSession final : public ServiceFramework<IPdCradleSession> {
public:
explicit IPdCradleSession() : ServiceFramework{"IPdCradleSession"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "VdmUserWrite"},
{1, nullptr, "VdmUserRead"},
{2, nullptr, "Vdm20Init"},
{3, nullptr, "GetFwType"},
{4, nullptr, "GetFwRevision"},
{5, nullptr, "GetManufacturerId"},
{6, nullptr, "GetDeviceId"},
{7, nullptr, "Unknown1"},
{8, nullptr, "Unknown2"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class USB_PD_C final : public ServiceFramework<USB_PD_C> {
public:
explicit USB_PD_C() : ServiceFramework{"usb:pd:c"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &USB_PD_C::GetPdCradleSession, "GetPdCradleSession"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetPdCradleSession(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IPdCradleSession>();
LOG_DEBUG(Service_USB, "called");
}
};
class USB_PM final : public ServiceFramework<USB_PM> {
public:
explicit USB_PM() : ServiceFramework{"usb:pm"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
{1, nullptr, "Unknown2"},
{2, nullptr, "Unknown3"},
{3, nullptr, "Unknown4"},
{4, nullptr, "Unknown5"},
{5, nullptr, "Unknown6"},
};
// clang-format on
RegisterHandlers(functions);
}
};
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<USB_DS>()->InstallAsService(sm);
std::make_shared<USB_HS>()->InstallAsService(sm);
std::make_shared<USB_PD>()->InstallAsService(sm);
std::make_shared<USB_PD_C>()->InstallAsService(sm);
std::make_shared<USB_PM>()->InstallAsService(sm);
}
} // namespace Service::USB

View File

@@ -1,15 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Service::SM {
class ServiceManager;
}
namespace Service::USB {
void InstallInterfaces(SM::ServiceManager& sm);
} // namespace Service::USB

View File

@@ -20,10 +20,6 @@ namespace Loader {
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
: AppLoader(std::move(file)) {}
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
FileSys::VirtualDir directory)
: AppLoader(directory->GetFile("main")), dir(std::move(directory)) {}
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) {
if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
return FileType::DeconstructedRomDirectory;
@@ -38,12 +34,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
return ResultStatus::ErrorAlreadyLoaded;
}
if (dir == nullptr) {
if (file == nullptr)
return ResultStatus::ErrorInvalidFormat;
dir = file->GetContainingDirectory();
}
const FileSys::VirtualDir dir = file->GetContainingDirectory();
const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
if (npdm == nullptr)
return ResultStatus::ErrorInvalidFormat;

View File

@@ -22,9 +22,6 @@ class AppLoader_DeconstructedRomDirectory final : public AppLoader {
public:
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file);
// Overload to accept exefs directory. Must contain 'main' and 'main.npdm'
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory);
/**
* Returns the type of the file
* @param file std::shared_ptr<VfsFile> open file
@@ -43,7 +40,6 @@ public:
private:
FileSys::ProgramMetadata metadata;
FileSys::VirtualFile romfs;
FileSys::VirtualDir dir;
};
} // namespace Loader

View File

@@ -22,8 +22,7 @@
namespace Loader {
AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_)
: AppLoader(std::move(file_)), nca(std::make_unique<FileSys::NCA>(file)) {}
AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
FileSys::NCA nca(file);
@@ -40,7 +39,8 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
return ResultStatus::ErrorAlreadyLoaded;
}
const auto result = nca->GetStatus();
nca = std::make_unique<FileSys::NCA>(file);
ResultStatus result = nca->GetStatus();
if (result != ResultStatus::Success) {
return result;
}
@@ -48,16 +48,44 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (nca->GetType() != FileSys::NCAContentType::Program)
return ResultStatus::ErrorInvalidFormat;
const auto exefs = nca->GetExeFS();
auto exefs = nca->GetExeFS();
if (exefs == nullptr)
return ResultStatus::ErrorInvalidFormat;
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs);
result = metadata.Load(exefs->GetFile("main.npdm"));
if (result != ResultStatus::Success) {
return result;
}
metadata.Print();
const auto load_result = directory_loader->Load(process);
if (load_result != ResultStatus::Success)
return load_result;
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
return ResultStatus::ErrorUnsupportedArch;
}
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const VAddr load_addr = next_load_addr;
next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr);
if (next_load_addr) {
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
} else {
next_load_addr = load_addr;
}
}
process->program_id = metadata.GetTitleID();
process->svc_access_mask.set();
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
metadata.GetMainThreadStackSize());
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));

View File

@@ -10,7 +10,6 @@
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/object.h"
#include "core/loader/loader.h"
#include "deconstructed_rom_directory.h"
namespace Loader {
@@ -42,7 +41,6 @@ private:
FileSys::ProgramMetadata metadata;
std::unique_ptr<FileSys::NCA> nca;
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
};
} // namespace Loader

View File

@@ -326,45 +326,43 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached)
}
void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
auto& system_instance = Core::System::GetInstance();
// Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
// null here
if (!system_instance.IsPoweredOn()) {
if (VideoCore::g_renderer == nullptr) {
return;
}
VAddr end = start + size;
const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
if (start >= region_end || end <= region_start) {
// No overlap with region
return;
}
const VAddr overlap_start = std::max(start, region_start);
const VAddr overlap_end = std::min(end, region_end);
VAddr overlap_start = std::max(start, region_start);
VAddr overlap_end = std::min(end, region_end);
const std::vector<Tegra::GPUVAddr> gpu_addresses =
system_instance.GPU().memory_manager->CpuToGpuAddress(overlap_start);
std::vector<Tegra::GPUVAddr> gpu_addresses =
Core::System::GetInstance().GPU().memory_manager->CpuToGpuAddress(overlap_start);
if (gpu_addresses.empty()) {
return;
}
const u64 overlap_size = overlap_end - overlap_start;
u64 overlap_size = overlap_end - overlap_start;
for (const auto& gpu_address : gpu_addresses) {
auto& rasterizer = system_instance.Renderer().Rasterizer();
auto* rasterizer = VideoCore::g_renderer->Rasterizer();
switch (mode) {
case FlushMode::Flush:
rasterizer.FlushRegion(gpu_address, overlap_size);
rasterizer->FlushRegion(gpu_address, overlap_size);
break;
case FlushMode::Invalidate:
rasterizer.InvalidateRegion(gpu_address, overlap_size);
rasterizer->InvalidateRegion(gpu_address, overlap_size);
break;
case FlushMode::FlushAndInvalidate:
rasterizer.FlushAndInvalidateRegion(gpu_address, overlap_size);
rasterizer->FlushAndInvalidateRegion(gpu_address, overlap_size);
break;
}
}

View File

@@ -140,10 +140,10 @@ void SetCurrentPageTable(PageTable* page_table);
PageTable* GetCurrentPageTable();
/// Determines if the given VAddr is valid for the specified process.
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
bool IsValidVirtualAddress(VAddr vaddr);
bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr);
bool IsValidVirtualAddress(const VAddr addr);
/// Determines if the given VAddr is a kernel address
bool IsKernelVirtualAddress(VAddr vaddr);
bool IsKernelVirtualAddress(const VAddr addr);
u8 Read8(VAddr addr);
u16 Read16(VAddr addr);
@@ -155,17 +155,18 @@ void Write16(VAddr addr, u16 data);
void Write32(VAddr addr, u32 data);
void Write64(VAddr addr, u64 data);
void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size);
void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size);
void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
size_t size);
void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size);
void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
size_t size);
void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size);
void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size);
void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size);
void ZeroBlock(const VAddr dest_addr, const size_t size);
void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
u8* GetPointer(VAddr vaddr);
u8* GetPointer(VAddr virtual_address);
std::string ReadCString(VAddr vaddr, std::size_t max_length);
std::string ReadCString(VAddr virtual_address, std::size_t max_length);
enum class FlushMode {
/// Write back modified surfaces to RAM
@@ -179,7 +180,7 @@ enum class FlushMode {
/**
* Mark each page touching the region as cached.
*/
void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached);
void RasterizerMarkRegionCached(Tegra::GPUVAddr start, u64 size, bool cached);
/**
* Flushes and invalidates any externally cached rasterizer resources touching the given virtual

View File

@@ -40,21 +40,22 @@ void PerfStats::EndGameFrame() {
game_frames += 1;
}
PerfStats::Results PerfStats::GetAndResetStats(microseconds current_system_time_us) {
PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) {
std::lock_guard<std::mutex> lock(object_mutex);
const auto now = Clock::now();
auto now = Clock::now();
// Walltime elapsed since stats were reset
const auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval;
auto system_us_per_second =
static_cast<double>(current_system_time_us - reset_point_system_us) / interval;
Results results{};
results.system_fps = static_cast<double>(system_frames) / interval;
results.game_fps = static_cast<double>(game_frames) / interval;
results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
static_cast<double>(system_frames);
results.emulation_speed = system_us_per_second.count() / 1'000'000.0;
results.emulation_speed = system_us_per_second / 1'000'000.0;
// Reset counters
reset_point = now;
@@ -73,10 +74,10 @@ double PerfStats::GetLastFrameTimeScale() {
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
}
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) {
// Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
// values increase the time needed to recover and limit framerate again after spikes.
constexpr microseconds MAX_LAG_TIME_US = 25us;
constexpr microseconds MAX_LAG_TIME_US = 25ms;
if (!Settings::values.toggle_framelimit) {
return;
@@ -84,7 +85,7 @@ void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
auto now = Clock::now();
frame_limiting_delta_err += current_system_time_us - previous_system_time_us;
frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us);
frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
frame_limiting_delta_err =
std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US);

View File

@@ -33,7 +33,7 @@ public:
void EndSystemFrame();
void EndGameFrame();
Results GetAndResetStats(std::chrono::microseconds current_system_time_us);
Results GetAndResetStats(u64 current_system_time_us);
/**
* Gets the ratio between walltime and the emulated time of the previous system frame. This is
@@ -47,7 +47,7 @@ private:
/// Point when the cumulative counters were reset
Clock::time_point reset_point = Clock::now();
/// System time when the cumulative counters were reset
std::chrono::microseconds reset_point_system_us{0};
u64 reset_point_system_us = 0;
/// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset
Clock::duration accumulated_frametime = Clock::duration::zero();
@@ -68,11 +68,11 @@ class FrameLimiter {
public:
using Clock = std::chrono::high_resolution_clock;
void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
void DoFrameLimiting(u64 current_system_time_us);
private:
/// Emulated system time (in microseconds) at the last limiter invocation
std::chrono::microseconds previous_system_time_us{0};
u64 previous_system_time_us = 0;
/// Walltime at the last limiter invocation
Clock::time_point previous_walltime = Clock::now();

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/hid/hid.h"
#include "core/settings.h"
@@ -20,9 +19,8 @@ void Apply() {
VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit;
auto& system_instance = Core::System::GetInstance();
if (system_instance.IsPoweredOn()) {
system_instance.Renderer().UpdateCurrentFramebufferLayout();
if (VideoCore::g_renderer) {
VideoCore::g_renderer->UpdateCurrentFramebufferLayout();
}
Service::HID::ReloadInputDevices();

View File

@@ -19,8 +19,8 @@ namespace Engines {
/// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00;
Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
: memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}
Maxwell3D::Maxwell3D(MemoryManager& memory_manager)
: memory_manager(memory_manager), macro_interpreter(*this) {}
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
auto macro_code = uploaded_macros.find(method);
@@ -130,7 +130,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
break;
}
rasterizer.NotifyMaxwellRegisterChanged(method);
VideoCore::g_renderer->Rasterizer()->NotifyMaxwellRegisterChanged(method);
if (debug_context) {
debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
@@ -218,7 +218,7 @@ void Maxwell3D::DrawArrays() {
}
const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
rasterizer.AccelerateDrawBatch(is_indexed);
VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(is_indexed);
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
// the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
@@ -285,6 +285,8 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
// TODO(Subv): Different data types for separate components are not supported
ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
// TODO(Subv): Only UNORM formats are supported for now.
ASSERT(r_type == Texture::ComponentType::UNORM);
return tic_entry;
}
@@ -391,7 +393,7 @@ void Maxwell3D::ProcessClearBuffers() {
regs.clear_buffers.R == regs.clear_buffers.B &&
regs.clear_buffers.R == regs.clear_buffers.A);
rasterizer.Clear();
VideoCore::g_renderer->Rasterizer()->Clear();
}
} // namespace Engines

View File

@@ -17,10 +17,6 @@
#include "video_core/memory_manager.h"
#include "video_core/textures/texture.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
#define MAXWELL3D_REG_INDEX(field_name) \
@@ -28,7 +24,7 @@ namespace Tegra::Engines {
class Maxwell3D final {
public:
explicit Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
explicit Maxwell3D(MemoryManager& memory_manager);
~Maxwell3D() = default;
/// Register structure of the Maxwell3D engine.
@@ -822,8 +818,6 @@ public:
Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const;
private:
VideoCore::RasterizerInterface& rasterizer;
std::unordered_map<u32, std::vector<u32>> uploaded_macros;
/// Macro method that is currently being executed / being fed parameters.

View File

@@ -7,13 +7,12 @@
#include "video_core/engines/maxwell_compute.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_interface.h"
namespace Tegra {
GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
GPU::GPU() {
memory_manager = std::make_unique<MemoryManager>();
maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);

View File

@@ -11,10 +11,6 @@
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "video_core/memory_manager.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra {
enum class RenderTargetFormat : u32 {
@@ -102,7 +98,7 @@ enum class EngineID {
class GPU final {
public:
explicit GPU(VideoCore::RasterizerInterface& rasterizer);
GPU();
~GPU();
/// Processes a command list stored at the specified address in GPU memory.

View File

@@ -7,8 +7,6 @@
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
namespace VideoCore {
RendererBase::RendererBase(EmuWindow& window) : render_window{window} {}
RendererBase::~RendererBase() = default;
@@ -23,5 +21,3 @@ void RendererBase::RefreshRasterizerSetting() {
rasterizer = std::make_unique<RasterizerOpenGL>(render_window);
}
}
} // namespace VideoCore

View File

@@ -13,8 +13,6 @@
class EmuWindow;
namespace VideoCore {
class RendererBase : NonCopyable {
public:
/// Used to reference a framebuffer
@@ -46,21 +44,15 @@ public:
return m_current_frame;
}
RasterizerInterface& Rasterizer() {
return *rasterizer;
}
const RasterizerInterface& Rasterizer() const {
return *rasterizer;
VideoCore::RasterizerInterface* Rasterizer() const {
return rasterizer.get();
}
void RefreshRasterizerSetting();
protected:
EmuWindow& render_window; ///< Reference to the render window handle.
std::unique_ptr<RasterizerInterface> rasterizer;
std::unique_ptr<VideoCore::RasterizerInterface> rasterizer;
f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
int m_current_frame = 0; ///< Current frame, should be set by the renderer
};
} // namespace VideoCore

View File

@@ -169,14 +169,8 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
ASSERT(buffer.IsEnabled());
glEnableVertexAttribArray(index);
if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
attrib.offset);
} else {
glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
}
glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
glVertexAttribBinding(index, attrib.buffer);
}

View File

@@ -46,8 +46,6 @@ struct FormatTuple {
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
params.unaligned_height = config.tic.Height();
params.size_in_bytes = params.SizeInBytes();
params.cache_width = Common::AlignUp(params.width, 16);
params.cache_height = Common::AlignUp(params.height, 16);
return params;
}
@@ -65,8 +63,6 @@ struct FormatTuple {
params.height = config.height;
params.unaligned_height = config.height;
params.size_in_bytes = params.SizeInBytes();
params.cache_width = Common::AlignUp(params.width, 16);
params.cache_height = Common::AlignUp(params.height, 16);
return params;
}
@@ -86,8 +82,6 @@ struct FormatTuple {
params.height = zeta_height;
params.unaligned_height = zeta_height;
params.size_in_bytes = params.SizeInBytes();
params.cache_width = Common::AlignUp(params.width, 16);
params.cache_height = Common::AlignUp(params.height, 16);
return params;
}
@@ -686,12 +680,12 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) {
// If use_accurate_framebuffers is enabled, always load from memory
FlushSurface(surface);
UnregisterSurface(surface);
} else if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
// Use the cached surface as-is
return surface;
} else {
} else if (surface->GetSurfaceParams() != params) {
// If surface parameters changed, recreate the surface from the old one
return RecreateSurface(surface, params);
} else {
// Use the cached surface as-is
return surface;
}
}

View File

@@ -9,7 +9,6 @@
#include <memory>
#include <vector>
#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/engines/maxwell_3d.h"
@@ -547,12 +546,6 @@ struct SurfaceParams {
return !operator==(other);
}
/// Checks if surfaces are compatible for caching
bool IsCompatibleSurface(const SurfaceParams& other) const {
return std::tie(pixel_format, type, cache_width, cache_height) ==
std::tie(other.pixel_format, other.type, other.cache_width, other.cache_height);
}
Tegra::GPUVAddr addr;
bool is_tiled;
u32 block_height;
@@ -563,10 +556,6 @@ struct SurfaceParams {
u32 height;
u32 unaligned_height;
size_t size_in_bytes;
// Parameters used for caching only
u32 cache_width;
u32 cache_height;
};
class CachedSurface final {

View File

@@ -766,16 +766,13 @@ private:
// goes into gpr28+0 and gpr28+1
size_t texs_offset{};
size_t src_elem{};
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
size_t dest_elem{};
for (unsigned elem = 0; elem < 2; ++elem) {
if (!instr.texs.IsComponentEnabled(src_elem++)) {
if (!instr.texs.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
}
regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false,
dest_elem++);
regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false, elem);
}
if (!instr.texs.HasTwoDestinations()) {

View File

@@ -103,7 +103,7 @@ ScopeAcquireGLContext::~ScopeAcquireGLContext() {
}
}
RendererOpenGL::RendererOpenGL(EmuWindow& window) : VideoCore::RendererBase{window} {}
RendererOpenGL::RendererOpenGL(EmuWindow& window) : RendererBase{window} {}
RendererOpenGL::~RendererOpenGL() = default;
/// Swap buffers (render frame)
@@ -160,8 +160,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
// only allows rows to have a memory alignement of 4.
ASSERT(framebuffer.stride % 4 == 0);
if (!rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride,
screen_info)) {
if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride,
screen_info)) {
// Reset the screen info's display texture to its own permanent texture
screen_info.display_texture = screen_info.texture.resource.handle;

View File

@@ -41,7 +41,7 @@ private:
EmuWindow& emu_window;
};
class RendererOpenGL : public VideoCore::RendererBase {
class RendererOpenGL : public RendererBase {
public:
explicit RendererOpenGL(EmuWindow& window);
~RendererOpenGL() override;

View File

@@ -3,16 +3,37 @@
// Refer to the license.txt file included.
#include <memory>
#include "common/logging/log.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
#include "video_core/video_core.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Video Core namespace
namespace VideoCore {
std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
std::atomic<bool> g_toggle_framelimit_enabled;
std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window) {
return std::make_unique<RendererOpenGL>(emu_window);
/// Initialize the video core
bool Init(EmuWindow& emu_window) {
g_renderer = std::make_unique<RendererOpenGL>(emu_window);
if (g_renderer->Init()) {
LOG_DEBUG(Render, "initialized OK");
} else {
LOG_CRITICAL(Render, "initialization failed !");
return false;
}
return true;
}
/// Shutdown the video core
void Shutdown() {
g_renderer.reset();
LOG_DEBUG(Render, "shutdown OK");
}
} // namespace VideoCore

View File

@@ -8,23 +8,25 @@
#include <memory>
class EmuWindow;
class RendererBase;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Video Core namespace
namespace VideoCore {
class RendererBase;
enum class Renderer { Software, OpenGL };
extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
// qt ui)
extern std::atomic<bool> g_toggle_framelimit_enabled;
/**
* Creates a renderer instance.
*
* @note The returned renderer instance is simply allocated. Its Init()
* function still needs to be called to fully complete its setup.
*/
std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window);
/// Initialize the video core
bool Init(EmuWindow& emu_window);
/// Shutdown the video core
void Shutdown();
} // namespace VideoCore

View File

@@ -15,4 +15,4 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
Common::g_scm_desc, QString(Common::g_build_date).left(10)));
}
AboutDialog::~AboutDialog() = default;
AboutDialog::~AboutDialog() {}

View File

@@ -16,7 +16,7 @@ class AboutDialog : public QDialog {
public:
explicit AboutDialog(QWidget* parent);
~AboutDialog() override;
~AboutDialog();
private:
std::unique_ptr<Ui::AboutDialog> ui;

View File

@@ -106,7 +106,7 @@ class GRenderWindow : public QWidget, public EmuWindow {
public:
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
~GRenderWindow() override;
~GRenderWindow();
// EmuWindow implementation
void SwapBuffers() override;

View File

@@ -24,7 +24,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
});
}
ConfigureDebug::~ConfigureDebug() = default;
ConfigureDebug::~ConfigureDebug() {}
void ConfigureDebug::setConfiguration() {
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);

View File

@@ -22,6 +22,13 @@
<string>GDB</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_1">
<property name="text">
<string>The GDB Stub only works correctly when the CPU JIT is off.</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_1">
<item>

View File

@@ -12,7 +12,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::
this->setConfiguration();
}
ConfigureDialog::~ConfigureDialog() = default;
ConfigureDialog::~ConfigureDialog() {}
void ConfigureDialog::setConfiguration() {}

View File

@@ -24,7 +24,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
}
ConfigureGeneral::~ConfigureGeneral() = default;
ConfigureGeneral::~ConfigureGeneral() {}
void ConfigureGeneral::setConfiguration() {
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);

View File

@@ -14,7 +14,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
this->setConfiguration();
}
ConfigureGraphics::~ConfigureGraphics() = default;
ConfigureGraphics::~ConfigureGraphics() {}
enum class Resolution : int {
Auto,

View File

@@ -35,7 +35,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
this->setConfiguration();
}
ConfigureSystem::~ConfigureSystem() = default;
ConfigureSystem::~ConfigureSystem() {}
void ConfigureSystem::setConfiguration() {
enabled = !Core::System::GetInstance().IsPoweredOn();

View File

@@ -34,8 +34,7 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
: QLabel(parent), surface_widget(surface_widget_) {}
SurfacePicture::~SurfacePicture() = default;
SurfacePicture::~SurfacePicture() {}
void SurfacePicture::mousePressEvent(QMouseEvent* event) {
// Only do something while the left mouse button is held down

View File

@@ -22,11 +22,11 @@ class SurfacePicture : public QLabel {
public:
explicit SurfacePicture(QWidget* parent = nullptr,
GraphicsSurfaceWidget* surface_widget = nullptr);
~SurfacePicture() override;
~SurfacePicture();
protected slots:
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
private:
GraphicsSurfaceWidget* surface_widget;

View File

@@ -14,7 +14,7 @@
#include "core/hle/kernel/timer.h"
#include "core/hle/kernel/wait_object.h"
WaitTreeItem::~WaitTreeItem() = default;
WaitTreeItem::~WaitTreeItem() {}
QColor WaitTreeItem::GetColor() const {
return QColor(Qt::GlobalColor::black);
@@ -316,7 +316,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
list.push_back(std::make_unique<WaitTreeText>(
tr("reset type = %1")
.arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).GetResetType()))));
.arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).reset_type))));
return list;
}

View File

@@ -25,13 +25,11 @@ class WaitTreeThread;
class WaitTreeItem : public QObject {
Q_OBJECT
public:
~WaitTreeItem() override;
virtual bool IsExpandable() const;
virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const;
virtual QString GetText() const = 0;
virtual QColor GetColor() const;
virtual ~WaitTreeItem();
void Expand();
WaitTreeItem* Parent() const;
const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const;

View File

@@ -162,15 +162,15 @@ void GameList::onTextChanged(const QString& newText) {
}
search_field->setFilterResult(rowCount, rowCount);
} else {
QStandardItem* child_file;
QString file_path, file_name, file_title, file_programmid;
int result_count = 0;
for (int i = 0; i < rowCount; ++i) {
const QStandardItem* child_file = item_model->item(i, 0);
const QString file_path =
child_file->data(GameListItemPath::FullPathRole).toString().toLower();
QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1);
const QString file_title =
child_file->data(GameListItemPath::TitleRole).toString().toLower();
const QString file_programmid =
child_file = item_model->item(i, 0);
file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower();
file_name = file_path.mid(file_path.lastIndexOf("/") + 1);
file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower();
file_programmid =
child_file->data(GameListItemPath::ProgramIdRole).toString().toLower();
// Only items which filename in combination with its title contains all words
@@ -258,20 +258,18 @@ void GameList::AddEntry(const QList<QStandardItem*>& entry_items) {
void GameList::ValidateEntry(const QModelIndex& item) {
// We don't care about the individual QStandardItem that was selected, but its row.
const int row = item_model->itemFromIndex(item)->row();
const QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
const QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
int row = item_model->itemFromIndex(item)->row();
QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
if (file_path.isEmpty())
return;
if (!QFileInfo::exists(file_path))
std::string std_file_path(file_path.toStdString());
if (!FileUtil::Exists(std_file_path))
return;
const QFileInfo file_info{file_path};
if (file_info.isDir()) {
const QDir dir{file_path};
const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (FileUtil::IsDirectory(std_file_path)) {
QDir dir(std_file_path.c_str());
QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (matching_main.size() == 1) {
emit GameChosen(dir.path() + DIR_SEP + matching_main[0]);
}
@@ -370,23 +368,21 @@ void GameList::LoadInterfaceLayout() {
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"};
static bool HasSupportedFileExtension(const std::string& file_name) {
const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
QFileInfo file = QFileInfo(file_name.c_str());
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
}
static bool IsExtractedNCAMain(const std::string& file_name) {
return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
return QFileInfo(file_name.c_str()).fileName() == "main";
}
static QString FormatGameName(const std::string& physical_name) {
const QString physical_name_as_qstring = QString::fromStdString(physical_name);
const QFileInfo file_info(physical_name_as_qstring);
QFileInfo file_info(physical_name.c_str());
if (IsExtractedNCAMain(physical_name)) {
return file_info.dir().path();
} else {
return QString::fromStdString(physical_name);
}
return physical_name_as_qstring;
}
void GameList::RefreshGameDirectory() {

View File

@@ -5,7 +5,6 @@
#pragma once
#include <atomic>
#include <utility>
#include <QImage>
#include <QRunnable>
#include <QStandardItem>
@@ -28,8 +27,9 @@ static QPixmap GetDefaultIcon(bool large) {
class GameListItem : public QStandardItem {
public:
GameListItem() = default;
explicit GameListItem(const QString& string) : QStandardItem(string) {}
GameListItem() : QStandardItem() {}
GameListItem(const QString& string) : QStandardItem(string) {}
virtual ~GameListItem() override {}
};
/**
@@ -45,8 +45,9 @@ public:
static const int TitleRole = Qt::UserRole + 2;
static const int ProgramIdRole = Qt::UserRole + 3;
GameListItemPath() = default;
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) {
GameListItemPath() : GameListItem() {}
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id)
: GameListItem() {
setData(game_path, FullPathRole);
setData(qulonglong(program_id), ProgramIdRole);
}
@@ -74,8 +75,8 @@ class GameListItemSize : public GameListItem {
public:
static const int SizeRole = Qt::UserRole + 1;
GameListItemSize() = default;
explicit GameListItemSize(const qulonglong size_bytes) {
GameListItemSize() : GameListItem() {}
GameListItemSize(const qulonglong size_bytes) : GameListItem() {
setData(size_bytes, SizeRole);
}
@@ -110,7 +111,7 @@ class GameListWorker : public QObject, public QRunnable {
public:
GameListWorker(QString dir_path, bool deep_scan)
: dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
: QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {}
public slots:
/// Starts the processing of directory tree information.

View File

@@ -81,8 +81,6 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
void GMainWindow::ShowCallouts() {}
const int GMainWindow::max_recent_files_item;
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
debug_context = Tegra::DebugContext::Construct();
@@ -479,7 +477,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
case Core::System::ResultStatus::ErrorVideoCore:
QMessageBox::critical(
this, tr("An error occurred initializing the video core."),
this, tr("An error occured in the video core."),
tr("yuzu has encountered an error while running the video core, please see the "
"log for more details."
"For more information on accessing the log, please see the following page: "
@@ -493,7 +491,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
default:
QMessageBox::critical(
this, tr("Error while loading ROM!"),
tr("An unknown error occurred. Please see the log for more details."));
tr("An unknown error occured. Please see the log for more details."));
break;
}
return false;
@@ -581,11 +579,11 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
}
void GMainWindow::UpdateRecentFiles() {
const int num_recent_files =
std::min(UISettings::values.recent_files.size(), max_recent_files_item);
unsigned int num_recent_files =
std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item));
for (int i = 0; i < num_recent_files; i++) {
const QString text = QString("&%1. %2").arg(i + 1).arg(
for (unsigned int i = 0; i < num_recent_files; i++) {
QString text = QString("&%1. %2").arg(i + 1).arg(
QFileInfo(UISettings::values.recent_files[i]).fileName());
actions_recent_files[i]->setText(text);
actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
@@ -597,8 +595,12 @@ void GMainWindow::UpdateRecentFiles() {
actions_recent_files[j]->setVisible(false);
}
// Enable the recent files menu if the list isn't empty
ui.menu_recent_files->setEnabled(num_recent_files != 0);
// Grey out the recent files menu if the list is empty
if (num_recent_files == 0) {
ui.menu_recent_files->setEnabled(false);
} else {
ui.menu_recent_files->setEnabled(true);
}
}
void GMainWindow::OnGameListLoadFile(QString game_path) {
@@ -629,15 +631,9 @@ void GMainWindow::OnMenuLoadFile() {
}
void GMainWindow::OnMenuLoadFolder() {
const QString dir_path =
QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
if (dir_path.isNull()) {
return;
}
const QDir dir{dir_path};
const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (matching_main.size() == 1) {
BootGame(dir.path() + DIR_SEP + matching_main[0]);
} else {
@@ -658,8 +654,9 @@ void GMainWindow::OnMenuRecentFile() {
QAction* action = qobject_cast<QAction*>(sender());
assert(action);
const QString filename = action->data().toString();
if (QFileInfo::exists(filename)) {
QString filename = action->data().toString();
QFileInfo file_info(filename);
if (file_info.exists()) {
BootGame(filename);
} else {
// Display an error message and remove the file from the list.
@@ -950,14 +947,15 @@ void GMainWindow::UpdateUITheme() {
QStringList theme_paths(default_theme_paths);
if (UISettings::values.theme != UISettings::themes[0].second &&
!UISettings::values.theme.isEmpty()) {
const QString theme_uri(":" + UISettings::values.theme + "/style.qss");
QString theme_uri(":" + UISettings::values.theme + "/style.qss");
QFile f(theme_uri);
if (f.open(QFile::ReadOnly | QFile::Text)) {
if (!f.exists()) {
LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
} else {
f.open(QFile::ReadOnly | QFile::Text);
QTextStream ts(&f);
qApp->setStyleSheet(ts.readAll());
GMainWindow::setStyleSheet(ts.readAll());
} else {
LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
}
theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme});
QIcon::setThemeName(":/icons/" + UISettings::values.theme);

View File

@@ -43,7 +43,7 @@ public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
GMainWindow();
~GMainWindow() override;
~GMainWindow();
signals:

View File

@@ -193,7 +193,7 @@ int main(int argc, char** argv) {
LOG_CRITICAL(Frontend, "Failed to determine system mode!");
return -1;
case Core::System::ResultStatus::ErrorVideoCore:
LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
LOG_CRITICAL(Frontend, "VideoCore not initialized");
return -1;
case Core::System::ResultStatus::Success:
break; // Expected case