diff options
Diffstat (limited to 'src')
75 files changed, 1386 insertions, 764 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index ec71524a3..82e4850f7 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -1,4 +1,8 @@ | |||
| 1 | add_library(audio_core STATIC | 1 | add_library(audio_core STATIC |
| 2 | algorithm/filter.cpp | ||
| 3 | algorithm/filter.h | ||
| 4 | algorithm/interpolate.cpp | ||
| 5 | algorithm/interpolate.h | ||
| 2 | audio_out.cpp | 6 | audio_out.cpp |
| 3 | audio_out.h | 7 | audio_out.h |
| 4 | audio_renderer.cpp | 8 | audio_renderer.cpp |
| @@ -7,12 +11,12 @@ add_library(audio_core STATIC | |||
| 7 | codec.cpp | 11 | codec.cpp |
| 8 | codec.h | 12 | codec.h |
| 9 | null_sink.h | 13 | null_sink.h |
| 10 | stream.cpp | ||
| 11 | stream.h | ||
| 12 | sink.h | 14 | sink.h |
| 13 | sink_details.cpp | 15 | sink_details.cpp |
| 14 | sink_details.h | 16 | sink_details.h |
| 15 | sink_stream.h | 17 | sink_stream.h |
| 18 | stream.cpp | ||
| 19 | stream.h | ||
| 16 | 20 | ||
| 17 | $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> | 21 | $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> |
| 18 | ) | 22 | ) |
diff --git a/src/audio_core/algorithm/filter.cpp b/src/audio_core/algorithm/filter.cpp new file mode 100644 index 000000000..403b8503f --- /dev/null +++ b/src/audio_core/algorithm/filter.cpp | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #define _USE_MATH_DEFINES | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <array> | ||
| 9 | #include <cmath> | ||
| 10 | #include <vector> | ||
| 11 | #include "audio_core/algorithm/filter.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | |||
| 14 | namespace AudioCore { | ||
| 15 | |||
| 16 | Filter Filter::LowPass(double cutoff, double Q) { | ||
| 17 | const double w0 = 2.0 * M_PI * cutoff; | ||
| 18 | const double sin_w0 = std::sin(w0); | ||
| 19 | const double cos_w0 = std::cos(w0); | ||
| 20 | const double alpha = sin_w0 / (2 * Q); | ||
| 21 | |||
| 22 | const double a0 = 1 + alpha; | ||
| 23 | const double a1 = -2.0 * cos_w0; | ||
| 24 | const double a2 = 1 - alpha; | ||
| 25 | const double b0 = 0.5 * (1 - cos_w0); | ||
| 26 | const double b1 = 1.0 * (1 - cos_w0); | ||
| 27 | const double b2 = 0.5 * (1 - cos_w0); | ||
| 28 | |||
| 29 | return {a0, a1, a2, b0, b1, b2}; | ||
| 30 | } | ||
| 31 | |||
| 32 | Filter::Filter() : Filter(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) {} | ||
| 33 | |||
| 34 | Filter::Filter(double a0, double a1, double a2, double b0, double b1, double b2) | ||
| 35 | : a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {} | ||
| 36 | |||
| 37 | void Filter::Process(std::vector<s16>& signal) { | ||
| 38 | const size_t num_frames = signal.size() / 2; | ||
| 39 | for (size_t i = 0; i < num_frames; i++) { | ||
| 40 | std::rotate(in.begin(), in.end() - 1, in.end()); | ||
| 41 | std::rotate(out.begin(), out.end() - 1, out.end()); | ||
| 42 | |||
| 43 | for (size_t ch = 0; ch < channel_count; ch++) { | ||
| 44 | in[0][ch] = signal[i * channel_count + ch]; | ||
| 45 | |||
| 46 | out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] - | ||
| 47 | a2 * out[2][ch]; | ||
| 48 | |||
| 49 | signal[i * 2 + ch] = std::clamp(out[0][ch], -32768.0, 32767.0); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Calculates the appropriate Q for each biquad in a cascading filter. | ||
| 55 | /// @param total_count The total number of biquads to be cascaded. | ||
| 56 | /// @param index 0-index of the biquad to calculate the Q value for. | ||
| 57 | static double CascadingBiquadQ(size_t total_count, size_t index) { | ||
| 58 | const double pole = M_PI * (2 * index + 1) / (4.0 * total_count); | ||
| 59 | return 1.0 / (2.0 * std::cos(pole)); | ||
| 60 | } | ||
| 61 | |||
| 62 | CascadingFilter CascadingFilter::LowPass(double cutoff, size_t cascade_size) { | ||
| 63 | std::vector<Filter> cascade(cascade_size); | ||
| 64 | for (size_t i = 0; i < cascade_size; i++) { | ||
| 65 | cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i)); | ||
| 66 | } | ||
| 67 | return CascadingFilter{std::move(cascade)}; | ||
| 68 | } | ||
| 69 | |||
| 70 | CascadingFilter::CascadingFilter() = default; | ||
| 71 | CascadingFilter::CascadingFilter(std::vector<Filter> filters) : filters(std::move(filters)) {} | ||
| 72 | |||
| 73 | void CascadingFilter::Process(std::vector<s16>& signal) { | ||
| 74 | for (auto& filter : filters) { | ||
| 75 | filter.Process(signal); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | } // namespace AudioCore | ||
diff --git a/src/audio_core/algorithm/filter.h b/src/audio_core/algorithm/filter.h new file mode 100644 index 000000000..a41beef98 --- /dev/null +++ b/src/audio_core/algorithm/filter.h | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <vector> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace AudioCore { | ||
| 12 | |||
| 13 | /// Digital biquad filter: | ||
| 14 | /// | ||
| 15 | /// b0 + b1 z^-1 + b2 z^-2 | ||
| 16 | /// H(z) = ------------------------ | ||
| 17 | /// a0 + a1 z^-1 + b2 z^-2 | ||
| 18 | class Filter { | ||
| 19 | public: | ||
| 20 | /// Creates a low-pass filter. | ||
| 21 | /// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0. | ||
| 22 | /// @param Q Determines the quality factor of this filter. | ||
| 23 | static Filter LowPass(double cutoff, double Q = 0.7071); | ||
| 24 | |||
| 25 | /// Passthrough filter. | ||
| 26 | Filter(); | ||
| 27 | |||
| 28 | Filter(double a0, double a1, double a2, double b0, double b1, double b2); | ||
| 29 | |||
| 30 | void Process(std::vector<s16>& signal); | ||
| 31 | |||
| 32 | private: | ||
| 33 | static constexpr size_t channel_count = 2; | ||
| 34 | |||
| 35 | /// Coefficients are in normalized form (a0 = 1.0). | ||
| 36 | double a1, a2, b0, b1, b2; | ||
| 37 | /// Input History | ||
| 38 | std::array<std::array<double, channel_count>, 3> in; | ||
| 39 | /// Output History | ||
| 40 | std::array<std::array<double, channel_count>, 3> out; | ||
| 41 | }; | ||
| 42 | |||
| 43 | /// Cascade filters to build up higher-order filters from lower-order ones. | ||
| 44 | class CascadingFilter { | ||
| 45 | public: | ||
| 46 | /// Creates a cascading low-pass filter. | ||
| 47 | /// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0. | ||
| 48 | /// @param cascade_size Number of biquads in cascade. | ||
| 49 | static CascadingFilter LowPass(double cutoff, size_t cascade_size); | ||
| 50 | |||
| 51 | /// Passthrough. | ||
| 52 | CascadingFilter(); | ||
| 53 | |||
| 54 | explicit CascadingFilter(std::vector<Filter> filters); | ||
| 55 | |||
| 56 | void Process(std::vector<s16>& signal); | ||
| 57 | |||
| 58 | private: | ||
| 59 | std::vector<Filter> filters; | ||
| 60 | }; | ||
| 61 | |||
| 62 | } // namespace AudioCore | ||
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp new file mode 100644 index 000000000..11459821f --- /dev/null +++ b/src/audio_core/algorithm/interpolate.cpp | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #define _USE_MATH_DEFINES | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <cmath> | ||
| 9 | #include <vector> | ||
| 10 | #include "audio_core/algorithm/interpolate.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | |||
| 14 | namespace AudioCore { | ||
| 15 | |||
| 16 | /// The Lanczos kernel | ||
| 17 | static double Lanczos(size_t a, double x) { | ||
| 18 | if (x == 0.0) | ||
| 19 | return 1.0; | ||
| 20 | const double px = M_PI * x; | ||
| 21 | return a * std::sin(px) * std::sin(px / a) / (px * px); | ||
| 22 | } | ||
| 23 | |||
| 24 | std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio) { | ||
| 25 | if (input.size() < 2) | ||
| 26 | return {}; | ||
| 27 | |||
| 28 | if (ratio <= 0) { | ||
| 29 | LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio); | ||
| 30 | ratio = 1.0; | ||
| 31 | } | ||
| 32 | |||
| 33 | if (ratio != state.current_ratio) { | ||
| 34 | const double cutoff_frequency = std::min(0.5 / ratio, 0.5 * ratio); | ||
| 35 | state.nyquist = CascadingFilter::LowPass(std::clamp(cutoff_frequency, 0.0, 0.4), 3); | ||
| 36 | state.current_ratio = ratio; | ||
| 37 | } | ||
| 38 | state.nyquist.Process(input); | ||
| 39 | |||
| 40 | constexpr size_t taps = InterpolationState::lanczos_taps; | ||
| 41 | const size_t num_frames = input.size() / 2; | ||
| 42 | |||
| 43 | std::vector<s16> output; | ||
| 44 | output.reserve(static_cast<size_t>(input.size() / ratio + 4)); | ||
| 45 | |||
| 46 | double& pos = state.position; | ||
| 47 | auto& h = state.history; | ||
| 48 | for (size_t i = 0; i < num_frames; ++i) { | ||
| 49 | std::rotate(h.begin(), h.end() - 1, h.end()); | ||
| 50 | h[0][0] = input[i * 2 + 0]; | ||
| 51 | h[0][1] = input[i * 2 + 1]; | ||
| 52 | |||
| 53 | while (pos <= 1.0) { | ||
| 54 | double l = 0.0; | ||
| 55 | double r = 0.0; | ||
| 56 | for (size_t j = 0; j < h.size(); j++) { | ||
| 57 | l += Lanczos(taps, pos + j - taps + 1) * h[j][0]; | ||
| 58 | r += Lanczos(taps, pos + j - taps + 1) * h[j][1]; | ||
| 59 | } | ||
| 60 | output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0))); | ||
| 61 | output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0))); | ||
| 62 | |||
| 63 | pos += ratio; | ||
| 64 | } | ||
| 65 | pos -= 1.0; | ||
| 66 | } | ||
| 67 | |||
| 68 | return output; | ||
| 69 | } | ||
| 70 | |||
| 71 | } // namespace AudioCore | ||
diff --git a/src/audio_core/algorithm/interpolate.h b/src/audio_core/algorithm/interpolate.h new file mode 100644 index 000000000..c79c2eef4 --- /dev/null +++ b/src/audio_core/algorithm/interpolate.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <vector> | ||
| 9 | #include "audio_core/algorithm/filter.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace AudioCore { | ||
| 13 | |||
| 14 | struct InterpolationState { | ||
| 15 | static constexpr size_t lanczos_taps = 4; | ||
| 16 | static constexpr size_t history_size = lanczos_taps * 2 - 1; | ||
| 17 | |||
| 18 | double current_ratio = 0.0; | ||
| 19 | CascadingFilter nyquist; | ||
| 20 | std::array<std::array<s16, 2>, history_size> history = {}; | ||
| 21 | double position = 0; | ||
| 22 | }; | ||
| 23 | |||
| 24 | /// Interpolates input signal to produce output signal. | ||
| 25 | /// @param input The signal to interpolate. | ||
| 26 | /// @param ratio Interpolation ratio. | ||
| 27 | /// ratio > 1.0 results in fewer output samples. | ||
| 28 | /// ratio < 1.0 results in more output samples. | ||
| 29 | /// @returns Output signal. | ||
| 30 | std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio); | ||
| 31 | |||
| 32 | /// Interpolates input signal to produce output signal. | ||
| 33 | /// @param input The signal to interpolate. | ||
| 34 | /// @param input_rate The sample rate of input. | ||
| 35 | /// @param output_rate The desired sample rate of the output. | ||
| 36 | /// @returns Output signal. | ||
| 37 | inline std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, | ||
| 38 | u32 input_rate, u32 output_rate) { | ||
| 39 | const double ratio = static_cast<double>(input_rate) / static_cast<double>(output_rate); | ||
| 40 | return Interpolate(state, std::move(input), ratio); | ||
| 41 | } | ||
| 42 | |||
| 43 | } // namespace AudioCore | ||
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 282f345c5..397b107f5 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "audio_core/algorithm/interpolate.h" | ||
| 5 | #include "audio_core/audio_renderer.h" | 6 | #include "audio_core/audio_renderer.h" |
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| @@ -26,6 +27,18 @@ AudioRenderer::AudioRenderer(AudioRendererParameter params, | |||
| 26 | QueueMixedBuffer(2); | 27 | QueueMixedBuffer(2); |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 30 | u32 AudioRenderer::GetSampleRate() const { | ||
| 31 | return worker_params.sample_rate; | ||
| 32 | } | ||
| 33 | |||
| 34 | u32 AudioRenderer::GetSampleCount() const { | ||
| 35 | return worker_params.sample_count; | ||
| 36 | } | ||
| 37 | |||
| 38 | u32 AudioRenderer::GetMixBufferCount() const { | ||
| 39 | return worker_params.mix_buffer_count; | ||
| 40 | } | ||
| 41 | |||
| 29 | std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { | 42 | std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { |
| 30 | // Copy UpdateDataHeader struct | 43 | // Copy UpdateDataHeader struct |
| 31 | UpdateDataHeader config{}; | 44 | UpdateDataHeader config{}; |
| @@ -187,6 +200,8 @@ void AudioRenderer::VoiceState::RefreshBuffer() { | |||
| 187 | break; | 200 | break; |
| 188 | } | 201 | } |
| 189 | 202 | ||
| 203 | samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE); | ||
| 204 | |||
| 190 | is_refresh_pending = false; | 205 | is_refresh_pending = false; |
| 191 | } | 206 | } |
| 192 | 207 | ||
| @@ -212,7 +227,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 212 | break; | 227 | break; |
| 213 | } | 228 | } |
| 214 | 229 | ||
| 215 | samples_remaining -= samples.size(); | 230 | samples_remaining -= samples.size() / stream->GetNumChannels(); |
| 216 | 231 | ||
| 217 | for (const auto& sample : samples) { | 232 | for (const auto& sample : samples) { |
| 218 | const s32 buffer_sample{buffer[offset]}; | 233 | const s32 buffer_sample{buffer[offset]}; |
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 6950a4681..eba67f28e 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | 10 | ||
| 11 | #include "audio_core/algorithm/interpolate.h" | ||
| 11 | #include "audio_core/audio_out.h" | 12 | #include "audio_core/audio_out.h" |
| 12 | #include "audio_core/codec.h" | 13 | #include "audio_core/codec.h" |
| 13 | #include "audio_core/stream.h" | 14 | #include "audio_core/stream.h" |
| @@ -26,7 +27,7 @@ enum class PlayState : u8 { | |||
| 26 | struct AudioRendererParameter { | 27 | struct AudioRendererParameter { |
| 27 | u32_le sample_rate; | 28 | u32_le sample_rate; |
| 28 | u32_le sample_count; | 29 | u32_le sample_count; |
| 29 | u32_le unknown_8; | 30 | u32_le mix_buffer_count; |
| 30 | u32_le unknown_c; | 31 | u32_le unknown_c; |
| 31 | u32_le voice_count; | 32 | u32_le voice_count; |
| 32 | u32_le sink_count; | 33 | u32_le sink_count; |
| @@ -160,6 +161,9 @@ public: | |||
| 160 | std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); | 161 | std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); |
| 161 | void QueueMixedBuffer(Buffer::Tag tag); | 162 | void QueueMixedBuffer(Buffer::Tag tag); |
| 162 | void ReleaseAndQueueBuffers(); | 163 | void ReleaseAndQueueBuffers(); |
| 164 | u32 GetSampleRate() const; | ||
| 165 | u32 GetSampleCount() const; | ||
| 166 | u32 GetMixBufferCount() const; | ||
| 163 | 167 | ||
| 164 | private: | 168 | private: |
| 165 | class VoiceState { | 169 | class VoiceState { |
| @@ -191,6 +195,7 @@ private: | |||
| 191 | size_t wave_index{}; | 195 | size_t wave_index{}; |
| 192 | size_t offset{}; | 196 | size_t offset{}; |
| 193 | Codec::ADPCMState adpcm_state{}; | 197 | Codec::ADPCMState adpcm_state{}; |
| 198 | InterpolationState interp_state{}; | ||
| 194 | std::vector<s16> samples; | 199 | std::vector<s16> samples; |
| 195 | VoiceOutStatus out_status{}; | 200 | VoiceOutStatus out_status{}; |
| 196 | VoiceInfo info{}; | 201 | VoiceInfo info{}; |
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 1501ef1f4..5a1177d0c 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include <mutex> | ||
| 7 | 8 | ||
| 8 | #include "audio_core/cubeb_sink.h" | 9 | #include "audio_core/cubeb_sink.h" |
| 9 | #include "audio_core/stream.h" | 10 | #include "audio_core/stream.h" |
| @@ -66,6 +67,8 @@ public: | |||
| 66 | return; | 67 | return; |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 70 | std::lock_guard lock{queue_mutex}; | ||
| 71 | |||
| 69 | queue.reserve(queue.size() + samples.size() * GetNumChannels()); | 72 | queue.reserve(queue.size() + samples.size() * GetNumChannels()); |
| 70 | 73 | ||
| 71 | if (is_6_channel) { | 74 | if (is_6_channel) { |
| @@ -94,6 +97,7 @@ private: | |||
| 94 | u32 num_channels{}; | 97 | u32 num_channels{}; |
| 95 | bool is_6_channel{}; | 98 | bool is_6_channel{}; |
| 96 | 99 | ||
| 100 | std::mutex queue_mutex; | ||
| 97 | std::vector<s16> queue; | 101 | std::vector<s16> queue; |
| 98 | 102 | ||
| 99 | static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, | 103 | static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, |
| @@ -153,6 +157,8 @@ long SinkStreamImpl::DataCallback(cubeb_stream* stream, void* user_data, const v | |||
| 153 | return {}; | 157 | return {}; |
| 154 | } | 158 | } |
| 155 | 159 | ||
| 160 | std::lock_guard lock{impl->queue_mutex}; | ||
| 161 | |||
| 156 | const size_t frames_to_write{ | 162 | const size_t frames_to_write{ |
| 157 | std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))}; | 163 | std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))}; |
| 158 | 164 | ||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 2ad456864..d9424ea91 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -29,8 +29,6 @@ add_library(common STATIC | |||
| 29 | assert.h | 29 | assert.h |
| 30 | bit_field.h | 30 | bit_field.h |
| 31 | bit_set.h | 31 | bit_set.h |
| 32 | break_points.cpp | ||
| 33 | break_points.h | ||
| 34 | cityhash.cpp | 32 | cityhash.cpp |
| 35 | cityhash.h | 33 | cityhash.h |
| 36 | color.h | 34 | color.h |
diff --git a/src/common/break_points.cpp b/src/common/break_points.cpp deleted file mode 100644 index fa367a4ca..000000000 --- a/src/common/break_points.cpp +++ /dev/null | |||
| @@ -1,90 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <sstream> | ||
| 7 | #include "common/break_points.h" | ||
| 8 | |||
| 9 | bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const { | ||
| 10 | auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; }; | ||
| 11 | auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); | ||
| 12 | return it != m_BreakPoints.end(); | ||
| 13 | } | ||
| 14 | |||
| 15 | bool BreakPoints::IsTempBreakPoint(u32 iAddress) const { | ||
| 16 | auto cond = [&iAddress](const TBreakPoint& bp) { | ||
| 17 | return bp.iAddress == iAddress && bp.bTemporary; | ||
| 18 | }; | ||
| 19 | auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); | ||
| 20 | return it != m_BreakPoints.end(); | ||
| 21 | } | ||
| 22 | |||
| 23 | BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const { | ||
| 24 | TBreakPointsStr bps; | ||
| 25 | for (auto breakpoint : m_BreakPoints) { | ||
| 26 | if (!breakpoint.bTemporary) { | ||
| 27 | std::stringstream bp; | ||
| 28 | bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : ""); | ||
| 29 | bps.push_back(bp.str()); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | return bps; | ||
| 34 | } | ||
| 35 | |||
| 36 | void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) { | ||
| 37 | for (auto bps_item : bps) { | ||
| 38 | TBreakPoint bp; | ||
| 39 | std::stringstream bpstr; | ||
| 40 | bpstr << std::hex << bps_item; | ||
| 41 | bpstr >> bp.iAddress; | ||
| 42 | bp.bOn = bps_item.find("n") != bps_item.npos; | ||
| 43 | bp.bTemporary = false; | ||
| 44 | Add(bp); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | void BreakPoints::Add(const TBreakPoint& bp) { | ||
| 49 | if (!IsAddressBreakPoint(bp.iAddress)) { | ||
| 50 | m_BreakPoints.push_back(bp); | ||
| 51 | // if (jit) | ||
| 52 | // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | void BreakPoints::Add(u32 em_address, bool temp) { | ||
| 57 | if (!IsAddressBreakPoint(em_address)) // only add new addresses | ||
| 58 | { | ||
| 59 | TBreakPoint pt; // breakpoint settings | ||
| 60 | pt.bOn = true; | ||
| 61 | pt.bTemporary = temp; | ||
| 62 | pt.iAddress = em_address; | ||
| 63 | |||
| 64 | m_BreakPoints.push_back(pt); | ||
| 65 | |||
| 66 | // if (jit) | ||
| 67 | // jit->GetBlockCache()->InvalidateICache(em_address, 4); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | void BreakPoints::Remove(u32 em_address) { | ||
| 72 | auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; }; | ||
| 73 | auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond); | ||
| 74 | if (it != m_BreakPoints.end()) | ||
| 75 | m_BreakPoints.erase(it); | ||
| 76 | } | ||
| 77 | |||
| 78 | void BreakPoints::Clear() { | ||
| 79 | // if (jit) | ||
| 80 | //{ | ||
| 81 | // std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(), | ||
| 82 | // [](const TBreakPoint& bp) | ||
| 83 | // { | ||
| 84 | // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4); | ||
| 85 | // } | ||
| 86 | // ); | ||
| 87 | //} | ||
| 88 | |||
| 89 | m_BreakPoints.clear(); | ||
| 90 | } | ||
diff --git a/src/common/break_points.h b/src/common/break_points.h deleted file mode 100644 index e15b9f842..000000000 --- a/src/common/break_points.h +++ /dev/null | |||
| @@ -1,49 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | #include <vector> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | class DebugInterface; | ||
| 12 | |||
| 13 | struct TBreakPoint { | ||
| 14 | u32 iAddress; | ||
| 15 | bool bOn; | ||
| 16 | bool bTemporary; | ||
| 17 | }; | ||
| 18 | |||
| 19 | // Code breakpoints. | ||
| 20 | class BreakPoints { | ||
| 21 | public: | ||
| 22 | typedef std::vector<TBreakPoint> TBreakPoints; | ||
| 23 | typedef std::vector<std::string> TBreakPointsStr; | ||
| 24 | |||
| 25 | const TBreakPoints& GetBreakPoints() { | ||
| 26 | return m_BreakPoints; | ||
| 27 | } | ||
| 28 | |||
| 29 | TBreakPointsStr GetStrings() const; | ||
| 30 | void AddFromStrings(const TBreakPointsStr& bps); | ||
| 31 | |||
| 32 | // is address breakpoint | ||
| 33 | bool IsAddressBreakPoint(u32 iAddress) const; | ||
| 34 | bool IsTempBreakPoint(u32 iAddress) const; | ||
| 35 | |||
| 36 | // Add BreakPoint | ||
| 37 | void Add(u32 em_address, bool temp = false); | ||
| 38 | void Add(const TBreakPoint& bp); | ||
| 39 | |||
| 40 | // Remove Breakpoint | ||
| 41 | void Remove(u32 iAddress); | ||
| 42 | void Clear(); | ||
| 43 | |||
| 44 | void DeleteByAddress(u32 Address); | ||
| 45 | |||
| 46 | private: | ||
| 47 | TBreakPoints m_BreakPoints; | ||
| 48 | u32 m_iBreakOnCount; | ||
| 49 | }; | ||
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index e80784c3c..1323f8d0f 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -302,13 +302,14 @@ Backend* GetBackend(std::string_view backend_name) { | |||
| 302 | void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, | 302 | void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, |
| 303 | unsigned int line_num, const char* function, const char* format, | 303 | unsigned int line_num, const char* function, const char* format, |
| 304 | const fmt::format_args& args) { | 304 | const fmt::format_args& args) { |
| 305 | auto filter = Impl::Instance().GetGlobalFilter(); | 305 | auto& instance = Impl::Instance(); |
| 306 | const auto& filter = instance.GetGlobalFilter(); | ||
| 306 | if (!filter.CheckMessage(log_class, log_level)) | 307 | if (!filter.CheckMessage(log_class, log_level)) |
| 307 | return; | 308 | return; |
| 308 | 309 | ||
| 309 | Entry entry = | 310 | Entry entry = |
| 310 | CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args)); | 311 | CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args)); |
| 311 | 312 | ||
| 312 | Impl::Instance().PushEntry(std::move(entry)); | 313 | instance.PushEntry(std::move(entry)); |
| 313 | } | 314 | } |
| 314 | } // namespace Log | 315 | } // namespace Log |
diff --git a/src/common/misc.cpp b/src/common/misc.cpp index 217a87098..3fa8a3bc4 100644 --- a/src/common/misc.cpp +++ b/src/common/misc.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstddef> | 5 | #include <cstddef> |
| 6 | #ifdef _WIN32 | 6 | #ifdef _WIN32 |
| 7 | #include <Windows.h> | 7 | #include <windows.h> |
| 8 | #else | 8 | #else |
| 9 | #include <cerrno> | 9 | #include <cerrno> |
| 10 | #include <cstring> | 10 | #include <cstring> |
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h index 38a450d69..133122c5f 100644 --- a/src/common/thread_queue_list.h +++ b/src/common/thread_queue_list.h | |||
| @@ -16,7 +16,7 @@ struct ThreadQueueList { | |||
| 16 | // (dynamically resizable) circular buffers to remove their overhead when | 16 | // (dynamically resizable) circular buffers to remove their overhead when |
| 17 | // inserting and popping. | 17 | // inserting and popping. |
| 18 | 18 | ||
| 19 | typedef unsigned int Priority; | 19 | using Priority = unsigned int; |
| 20 | 20 | ||
| 21 | // Number of priority levels. (Valid levels are [0..NUM_QUEUES).) | 21 | // Number of priority levels. (Valid levels are [0..NUM_QUEUES).) |
| 22 | static const Priority NUM_QUEUES = N; | 22 | static const Priority NUM_QUEUES = N; |
| @@ -26,9 +26,9 @@ struct ThreadQueueList { | |||
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | // Only for debugging, returns priority level. | 28 | // Only for debugging, returns priority level. |
| 29 | Priority contains(const T& uid) { | 29 | Priority contains(const T& uid) const { |
| 30 | for (Priority i = 0; i < NUM_QUEUES; ++i) { | 30 | for (Priority i = 0; i < NUM_QUEUES; ++i) { |
| 31 | Queue& cur = queues[i]; | 31 | const Queue& cur = queues[i]; |
| 32 | if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) { | 32 | if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) { |
| 33 | return i; | 33 | return i; |
| 34 | } | 34 | } |
| @@ -37,8 +37,8 @@ struct ThreadQueueList { | |||
| 37 | return -1; | 37 | return -1; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | T get_first() { | 40 | T get_first() const { |
| 41 | Queue* cur = first; | 41 | const Queue* cur = first; |
| 42 | while (cur != nullptr) { | 42 | while (cur != nullptr) { |
| 43 | if (!cur->data.empty()) { | 43 | if (!cur->data.empty()) { |
| 44 | return cur->data.front(); | 44 | return cur->data.front(); |
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h index fd3fbdd4b..927da9187 100644 --- a/src/common/x64/xbyak_abi.h +++ b/src/common/x64/xbyak_abi.h | |||
| @@ -9,10 +9,9 @@ | |||
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/bit_set.h" | 10 | #include "common/bit_set.h" |
| 11 | 11 | ||
| 12 | namespace Common { | 12 | namespace Common::X64 { |
| 13 | namespace X64 { | ||
| 14 | 13 | ||
| 15 | int RegToIndex(const Xbyak::Reg& reg) { | 14 | inline int RegToIndex(const Xbyak::Reg& reg) { |
| 16 | using Kind = Xbyak::Reg::Kind; | 15 | using Kind = Xbyak::Reg::Kind; |
| 17 | ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0, | 16 | ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0, |
| 18 | "RegSet only support GPRs and XMM registers."); | 17 | "RegSet only support GPRs and XMM registers."); |
| @@ -152,8 +151,8 @@ constexpr size_t ABI_SHADOW_SPACE = 0; | |||
| 152 | 151 | ||
| 153 | #endif | 152 | #endif |
| 154 | 153 | ||
| 155 | void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size, | 154 | inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size, |
| 156 | s32* out_subtraction, s32* out_xmm_offset) { | 155 | s32* out_subtraction, s32* out_xmm_offset) { |
| 157 | int count = (regs & ABI_ALL_GPRS).Count(); | 156 | int count = (regs & ABI_ALL_GPRS).Count(); |
| 158 | rsp_alignment -= count * 8; | 157 | rsp_alignment -= count * 8; |
| 159 | size_t subtraction = 0; | 158 | size_t subtraction = 0; |
| @@ -174,8 +173,8 @@ void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_f | |||
| 174 | *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); | 173 | *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); |
| 175 | } | 174 | } |
| 176 | 175 | ||
| 177 | size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, | 176 | inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, |
| 178 | size_t rsp_alignment, size_t needed_frame_size = 0) { | 177 | size_t rsp_alignment, size_t needed_frame_size = 0) { |
| 179 | s32 subtraction, xmm_offset; | 178 | s32 subtraction, xmm_offset; |
| 180 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | 179 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); |
| 181 | 180 | ||
| @@ -195,8 +194,8 @@ size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs | |||
| 195 | return ABI_SHADOW_SPACE; | 194 | return ABI_SHADOW_SPACE; |
| 196 | } | 195 | } |
| 197 | 196 | ||
| 198 | void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, size_t rsp_alignment, | 197 | inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, |
| 199 | size_t needed_frame_size = 0) { | 198 | size_t rsp_alignment, size_t needed_frame_size = 0) { |
| 200 | s32 subtraction, xmm_offset; | 199 | s32 subtraction, xmm_offset; |
| 201 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | 200 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); |
| 202 | 201 | ||
| @@ -217,5 +216,4 @@ void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, s | |||
| 217 | } | 216 | } |
| 218 | } | 217 | } |
| 219 | 218 | ||
| 220 | } // namespace X64 | 219 | } // namespace Common::X64 |
| 221 | } // namespace Common | ||
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h index ec76e0a47..02323a017 100644 --- a/src/common/x64/xbyak_util.h +++ b/src/common/x64/xbyak_util.h | |||
| @@ -8,8 +8,7 @@ | |||
| 8 | #include <xbyak.h> | 8 | #include <xbyak.h> |
| 9 | #include "common/x64/xbyak_abi.h" | 9 | #include "common/x64/xbyak_abi.h" |
| 10 | 10 | ||
| 11 | namespace Common { | 11 | namespace Common::X64 { |
| 12 | namespace X64 { | ||
| 13 | 12 | ||
| 14 | // Constants for use with cmpps/cmpss | 13 | // Constants for use with cmpps/cmpss |
| 15 | enum { | 14 | enum { |
| @@ -45,5 +44,4 @@ inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { | |||
| 45 | } | 44 | } |
| 46 | } | 45 | } |
| 47 | 46 | ||
| 48 | } // namespace X64 | 47 | } // namespace Common::X64 |
| 49 | } // namespace Common | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8cf9fb405..67ad6109a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -257,6 +257,10 @@ add_library(core STATIC | |||
| 257 | hle/service/nvdrv/devices/nvhost_gpu.h | 257 | hle/service/nvdrv/devices/nvhost_gpu.h |
| 258 | hle/service/nvdrv/devices/nvhost_nvdec.cpp | 258 | hle/service/nvdrv/devices/nvhost_nvdec.cpp |
| 259 | hle/service/nvdrv/devices/nvhost_nvdec.h | 259 | hle/service/nvdrv/devices/nvhost_nvdec.h |
| 260 | hle/service/nvdrv/devices/nvhost_nvjpg.cpp | ||
| 261 | hle/service/nvdrv/devices/nvhost_nvjpg.h | ||
| 262 | hle/service/nvdrv/devices/nvhost_vic.cpp | ||
| 263 | hle/service/nvdrv/devices/nvhost_vic.h | ||
| 260 | hle/service/nvdrv/devices/nvmap.cpp | 264 | hle/service/nvdrv/devices/nvmap.cpp |
| 261 | hle/service/nvdrv/devices/nvmap.h | 265 | hle/service/nvdrv/devices/nvmap.h |
| 262 | hle/service/nvdrv/interface.cpp | 266 | hle/service/nvdrv/interface.cpp |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index ceb3f7683..20e5200a8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -86,7 +86,16 @@ public: | |||
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | void AddTicks(u64 ticks) override { | 88 | void AddTicks(u64 ticks) override { |
| 89 | CoreTiming::AddTicks(ticks - num_interpreted_instructions); | 89 | // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a |
| 90 | // rough approximation of the amount of executed ticks in the system, it may be thrown off | ||
| 91 | // if not all cores are doing a similar amount of work. Instead of doing this, we should | ||
| 92 | // device a way so that timing is consistent across all cores without increasing the ticks 4 | ||
| 93 | // times. | ||
| 94 | u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; | ||
| 95 | // Always execute at least one tick. | ||
| 96 | amortized_ticks = std::max<u64>(amortized_ticks, 1); | ||
| 97 | |||
| 98 | CoreTiming::AddTicks(amortized_ticks); | ||
| 90 | num_interpreted_instructions = 0; | 99 | num_interpreted_instructions = 0; |
| 91 | } | 100 | } |
| 92 | u64 GetTicksRemaining() override { | 101 | u64 GetTicksRemaining() override { |
| @@ -234,9 +243,7 @@ void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { | |||
| 234 | } | 243 | } |
| 235 | 244 | ||
| 236 | void ARM_Dynarmic::PrepareReschedule() { | 245 | void ARM_Dynarmic::PrepareReschedule() { |
| 237 | if (jit->IsExecuting()) { | 246 | jit->HaltExecution(); |
| 238 | jit->HaltExecution(); | ||
| 239 | } | ||
| 240 | } | 247 | } |
| 241 | 248 | ||
| 242 | void ARM_Dynarmic::ClearInstructionCache() { | 249 | void ARM_Dynarmic::ClearInstructionCache() { |
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index 46a522fcd..b042ee02b 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 15 | #include "core/hle/kernel/scheduler.h" | 15 | #include "core/hle/kernel/scheduler.h" |
| 16 | #include "core/hle/kernel/thread.h" | 16 | #include "core/hle/kernel/thread.h" |
| 17 | #include "core/hle/lock.h" | ||
| 17 | #include "core/settings.h" | 18 | #include "core/settings.h" |
| 18 | 19 | ||
| 19 | namespace Core { | 20 | namespace Core { |
| @@ -90,6 +91,7 @@ void Cpu::RunLoop(bool tight_loop) { | |||
| 90 | LOG_TRACE(Core, "Core-{} idling", core_index); | 91 | LOG_TRACE(Core, "Core-{} idling", core_index); |
| 91 | 92 | ||
| 92 | if (IsMainCore()) { | 93 | if (IsMainCore()) { |
| 94 | // TODO(Subv): Only let CoreTiming idle if all 4 cores are idling. | ||
| 93 | CoreTiming::Idle(); | 95 | CoreTiming::Idle(); |
| 94 | CoreTiming::Advance(); | 96 | CoreTiming::Advance(); |
| 95 | } | 97 | } |
| @@ -125,6 +127,8 @@ void Cpu::Reschedule() { | |||
| 125 | } | 127 | } |
| 126 | 128 | ||
| 127 | reschedule_pending = false; | 129 | reschedule_pending = false; |
| 130 | // Lock the global kernel mutex when we manipulate the HLE state | ||
| 131 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 128 | scheduler->Reschedule(); | 132 | scheduler->Reschedule(); |
| 129 | } | 133 | } |
| 130 | 134 | ||
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h index 976952903..56cdae194 100644 --- a/src/core/core_cpu.h +++ b/src/core/core_cpu.h | |||
| @@ -79,7 +79,7 @@ private: | |||
| 79 | std::shared_ptr<CpuBarrier> cpu_barrier; | 79 | std::shared_ptr<CpuBarrier> cpu_barrier; |
| 80 | std::shared_ptr<Kernel::Scheduler> scheduler; | 80 | std::shared_ptr<Kernel::Scheduler> scheduler; |
| 81 | 81 | ||
| 82 | bool reschedule_pending{}; | 82 | std::atomic<bool> reschedule_pending = false; |
| 83 | size_t core_index; | 83 | size_t core_index; |
| 84 | }; | 84 | }; |
| 85 | 85 | ||
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index d3bb6f818..7953c8720 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -56,6 +56,9 @@ static u64 event_fifo_id; | |||
| 56 | // to the event_queue by the emu thread | 56 | // to the event_queue by the emu thread |
| 57 | static Common::MPSCQueue<Event, false> ts_queue; | 57 | static Common::MPSCQueue<Event, false> ts_queue; |
| 58 | 58 | ||
| 59 | // the queue for unscheduling the events from other threads threadsafe | ||
| 60 | static Common::MPSCQueue<std::pair<const EventType*, u64>, false> unschedule_queue; | ||
| 61 | |||
| 59 | constexpr int MAX_SLICE_LENGTH = 20000; | 62 | constexpr int MAX_SLICE_LENGTH = 20000; |
| 60 | 63 | ||
| 61 | static s64 idled_cycles; | 64 | static s64 idled_cycles; |
| @@ -135,11 +138,9 @@ void ClearPendingEvents() { | |||
| 135 | void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { | 138 | void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { |
| 136 | ASSERT(event_type != nullptr); | 139 | ASSERT(event_type != nullptr); |
| 137 | s64 timeout = GetTicks() + cycles_into_future; | 140 | s64 timeout = GetTicks() + cycles_into_future; |
| 138 | |||
| 139 | // If this event needs to be scheduled before the next advance(), force one early | 141 | // If this event needs to be scheduled before the next advance(), force one early |
| 140 | if (!is_global_timer_sane) | 142 | if (!is_global_timer_sane) |
| 141 | ForceExceptionCheck(cycles_into_future); | 143 | ForceExceptionCheck(cycles_into_future); |
| 142 | |||
| 143 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); | 144 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); |
| 144 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 145 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 145 | } | 146 | } |
| @@ -160,6 +161,10 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) { | |||
| 160 | } | 161 | } |
| 161 | } | 162 | } |
| 162 | 163 | ||
| 164 | void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) { | ||
| 165 | unschedule_queue.Push(std::make_pair(event_type, userdata)); | ||
| 166 | } | ||
| 167 | |||
| 163 | void RemoveEvent(const EventType* event_type) { | 168 | void RemoveEvent(const EventType* event_type) { |
| 164 | auto itr = std::remove_if(event_queue.begin(), event_queue.end(), | 169 | auto itr = std::remove_if(event_queue.begin(), event_queue.end(), |
| 165 | [&](const Event& e) { return e.type == event_type; }); | 170 | [&](const Event& e) { return e.type == event_type; }); |
| @@ -196,6 +201,9 @@ void MoveEvents() { | |||
| 196 | 201 | ||
| 197 | void Advance() { | 202 | void Advance() { |
| 198 | MoveEvents(); | 203 | MoveEvents(); |
| 204 | for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) { | ||
| 205 | UnscheduleEvent(ev.first, ev.second); | ||
| 206 | } | ||
| 199 | 207 | ||
| 200 | int cycles_executed = slice_length - downcount; | 208 | int cycles_executed = slice_length - downcount; |
| 201 | global_timer += cycles_executed; | 209 | global_timer += cycles_executed; |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index dfa161c0d..9ed757bd7 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -65,6 +65,7 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user | |||
| 65 | void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata); | 65 | void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata); |
| 66 | 66 | ||
| 67 | void UnscheduleEvent(const EventType* event_type, u64 userdata); | 67 | void UnscheduleEvent(const EventType* event_type, u64 userdata); |
| 68 | void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata); | ||
| 68 | 69 | ||
| 69 | /// We only permit one event of each type in the queue at a time. | 70 | /// We only permit one event of each type in the queue at a time. |
| 70 | void RemoveEvent(const EventType* event_type); | 71 | void RemoveEvent(const EventType* event_type); |
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 093c625ff..1d7c7fb10 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -4,11 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <string> | 6 | #include <string> |
| 7 | #include <core/loader/loader.h> | 7 | |
| 8 | #include <fmt/ostream.h> | ||
| 9 | |||
| 8 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 9 | #include "core/file_sys/card_image.h" | 11 | #include "core/file_sys/card_image.h" |
| 10 | #include "core/file_sys/partition_filesystem.h" | 12 | #include "core/file_sys/partition_filesystem.h" |
| 11 | #include "core/file_sys/vfs_offset.h" | 13 | #include "core/file_sys/vfs_offset.h" |
| 14 | #include "core/loader/loader.h" | ||
| 12 | 15 | ||
| 13 | namespace FileSys { | 16 | namespace FileSys { |
| 14 | 17 | ||
| @@ -111,19 +114,19 @@ VirtualFile XCI::GetNCAFileByType(NCAContentType type) const { | |||
| 111 | return nullptr; | 114 | return nullptr; |
| 112 | } | 115 | } |
| 113 | 116 | ||
| 114 | std::vector<std::shared_ptr<VfsFile>> XCI::GetFiles() const { | 117 | std::vector<VirtualFile> XCI::GetFiles() const { |
| 115 | return {}; | 118 | return {}; |
| 116 | } | 119 | } |
| 117 | 120 | ||
| 118 | std::vector<std::shared_ptr<VfsDirectory>> XCI::GetSubdirectories() const { | 121 | std::vector<VirtualDir> XCI::GetSubdirectories() const { |
| 119 | return std::vector<std::shared_ptr<VfsDirectory>>(); | 122 | return {}; |
| 120 | } | 123 | } |
| 121 | 124 | ||
| 122 | std::string XCI::GetName() const { | 125 | std::string XCI::GetName() const { |
| 123 | return file->GetName(); | 126 | return file->GetName(); |
| 124 | } | 127 | } |
| 125 | 128 | ||
| 126 | std::shared_ptr<VfsDirectory> XCI::GetParentDirectory() const { | 129 | VirtualDir XCI::GetParentDirectory() const { |
| 127 | return file->GetContainingDirectory(); | 130 | return file->GetContainingDirectory(); |
| 128 | } | 131 | } |
| 129 | 132 | ||
| @@ -146,7 +149,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | |||
| 146 | const u16 error_id = static_cast<u16>(nca->GetStatus()); | 149 | const u16 error_id = static_cast<u16>(nca->GetStatus()); |
| 147 | LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})", | 150 | LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})", |
| 148 | partition_names[static_cast<size_t>(part)], nca->GetName(), error_id, | 151 | partition_names[static_cast<size_t>(part)], nca->GetName(), error_id, |
| 149 | Loader::GetMessageForResultStatus(nca->GetStatus())); | 152 | nca->GetStatus()); |
| 150 | } | 153 | } |
| 151 | } | 154 | } |
| 152 | 155 | ||
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 3514bdf6c..a03d5264e 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h | |||
| @@ -72,13 +72,13 @@ public: | |||
| 72 | std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; | 72 | std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; |
| 73 | VirtualFile GetNCAFileByType(NCAContentType type) const; | 73 | VirtualFile GetNCAFileByType(NCAContentType type) const; |
| 74 | 74 | ||
| 75 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | 75 | std::vector<VirtualFile> GetFiles() const override; |
| 76 | 76 | ||
| 77 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | 77 | std::vector<VirtualDir> GetSubdirectories() const override; |
| 78 | 78 | ||
| 79 | std::string GetName() const override; | 79 | std::string GetName() const override; |
| 80 | 80 | ||
| 81 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | 81 | VirtualDir GetParentDirectory() const override; |
| 82 | 82 | ||
| 83 | protected: | 83 | protected: |
| 84 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | 84 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; |
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index 7c7a75816..be7bc32a8 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | #include "core/file_sys/vfs.h" | 13 | #include "core/file_sys/vfs.h" |
| 14 | 14 | ||
| 15 | namespace Loader { | 15 | namespace Loader { |
| 16 | enum class ResultStatus; | 16 | enum class ResultStatus : u16; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace FileSys { | 19 | namespace FileSys { |
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 06a7315db..74a91052b 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | #include "partition_filesystem.h" | 13 | #include "partition_filesystem.h" |
| 14 | 14 | ||
| 15 | namespace Loader { | 15 | namespace Loader { |
| 16 | enum class ResultStatus; | 16 | enum class ResultStatus : u16; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace FileSys { | 19 | namespace FileSys { |
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 24e158962..a5ec50b1a 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -74,15 +74,15 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view | |||
| 74 | return new_file; | 74 | return new_file; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { | 77 | VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { |
| 78 | const auto old_path = FileUtil::SanitizePath(old_path_); | 78 | const auto sanitized_old_path = FileUtil::SanitizePath(old_path); |
| 79 | const auto new_path = FileUtil::SanitizePath(new_path_); | 79 | const auto sanitized_new_path = FileUtil::SanitizePath(new_path); |
| 80 | 80 | ||
| 81 | // Again, non-default impls are highly encouraged to provide a more optimized version of this. | 81 | // Again, non-default impls are highly encouraged to provide a more optimized version of this. |
| 82 | auto out = CopyFile(old_path_, new_path_); | 82 | auto out = CopyFile(sanitized_old_path, sanitized_new_path); |
| 83 | if (out == nullptr) | 83 | if (out == nullptr) |
| 84 | return nullptr; | 84 | return nullptr; |
| 85 | if (DeleteFile(old_path)) | 85 | if (DeleteFile(sanitized_old_path)) |
| 86 | return out; | 86 | return out; |
| 87 | return nullptr; | 87 | return nullptr; |
| 88 | } | 88 | } |
| @@ -137,15 +137,15 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_ | |||
| 137 | return new_dir; | 137 | return new_dir; |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) { | 140 | VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { |
| 141 | const auto old_path = FileUtil::SanitizePath(old_path_); | 141 | const auto sanitized_old_path = FileUtil::SanitizePath(old_path); |
| 142 | const auto new_path = FileUtil::SanitizePath(new_path_); | 142 | const auto sanitized_new_path = FileUtil::SanitizePath(new_path); |
| 143 | 143 | ||
| 144 | // Non-default impls are highly encouraged to provide a more optimized version of this. | 144 | // Non-default impls are highly encouraged to provide a more optimized version of this. |
| 145 | auto out = CopyDirectory(old_path_, new_path_); | 145 | auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); |
| 146 | if (out == nullptr) | 146 | if (out == nullptr) |
| 147 | return nullptr; | 147 | return nullptr; |
| 148 | if (DeleteDirectory(old_path)) | 148 | if (DeleteDirectory(sanitized_old_path)) |
| 149 | return out; | 149 | return out; |
| 150 | return nullptr; | 150 | return nullptr; |
| 151 | } | 151 | } |
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 141a053ce..78a63c59b 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h | |||
| @@ -15,9 +15,9 @@ | |||
| 15 | 15 | ||
| 16 | namespace FileSys { | 16 | namespace FileSys { |
| 17 | 17 | ||
| 18 | struct VfsFilesystem; | 18 | class VfsDirectory; |
| 19 | struct VfsFile; | 19 | class VfsFile; |
| 20 | struct VfsDirectory; | 20 | class VfsFilesystem; |
| 21 | 21 | ||
| 22 | // Convenience typedefs to use Vfs* interfaces | 22 | // Convenience typedefs to use Vfs* interfaces |
| 23 | using VirtualFilesystem = std::shared_ptr<VfsFilesystem>; | 23 | using VirtualFilesystem = std::shared_ptr<VfsFilesystem>; |
| @@ -34,8 +34,9 @@ enum class VfsEntryType { | |||
| 34 | // A class representing an abstract filesystem. A default implementation given the root VirtualDir | 34 | // A class representing an abstract filesystem. A default implementation given the root VirtualDir |
| 35 | // is provided for convenience, but if the Vfs implementation has any additional state or | 35 | // is provided for convenience, but if the Vfs implementation has any additional state or |
| 36 | // functionality, they will need to override. | 36 | // functionality, they will need to override. |
| 37 | struct VfsFilesystem : NonCopyable { | 37 | class VfsFilesystem : NonCopyable { |
| 38 | VfsFilesystem(VirtualDir root); | 38 | public: |
| 39 | explicit VfsFilesystem(VirtualDir root); | ||
| 39 | virtual ~VfsFilesystem(); | 40 | virtual ~VfsFilesystem(); |
| 40 | 41 | ||
| 41 | // Gets the friendly name for the filesystem. | 42 | // Gets the friendly name for the filesystem. |
| @@ -81,7 +82,8 @@ protected: | |||
| 81 | }; | 82 | }; |
| 82 | 83 | ||
| 83 | // A class representing a file in an abstract filesystem. | 84 | // A class representing a file in an abstract filesystem. |
| 84 | struct VfsFile : NonCopyable { | 85 | class VfsFile : NonCopyable { |
| 86 | public: | ||
| 85 | virtual ~VfsFile(); | 87 | virtual ~VfsFile(); |
| 86 | 88 | ||
| 87 | // Retrieves the file name. | 89 | // Retrieves the file name. |
| @@ -179,7 +181,8 @@ struct VfsFile : NonCopyable { | |||
| 179 | }; | 181 | }; |
| 180 | 182 | ||
| 181 | // A class representing a directory in an abstract filesystem. | 183 | // A class representing a directory in an abstract filesystem. |
| 182 | struct VfsDirectory : NonCopyable { | 184 | class VfsDirectory : NonCopyable { |
| 185 | public: | ||
| 183 | virtual ~VfsDirectory(); | 186 | virtual ~VfsDirectory(); |
| 184 | 187 | ||
| 185 | // Retrives the file located at path as if the current directory was root. Returns nullptr if | 188 | // Retrives the file located at path as if the current directory was root. Returns nullptr if |
| @@ -295,7 +298,8 @@ protected: | |||
| 295 | 298 | ||
| 296 | // A convenience partial-implementation of VfsDirectory that stubs out methods that should only work | 299 | // A convenience partial-implementation of VfsDirectory that stubs out methods that should only work |
| 297 | // if writable. This is to avoid redundant empty methods everywhere. | 300 | // if writable. This is to avoid redundant empty methods everywhere. |
| 298 | struct ReadOnlyVfsDirectory : public VfsDirectory { | 301 | class ReadOnlyVfsDirectory : public VfsDirectory { |
| 302 | public: | ||
| 299 | bool IsWritable() const override; | 303 | bool IsWritable() const override; |
| 300 | bool IsReadable() const override; | 304 | bool IsReadable() const override; |
| 301 | std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override; | 305 | std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override; |
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h index 235970dc5..cb92d1570 100644 --- a/src/core/file_sys/vfs_offset.h +++ b/src/core/file_sys/vfs_offset.h | |||
| @@ -15,7 +15,8 @@ namespace FileSys { | |||
| 15 | // Similar to seeking to an offset. | 15 | // Similar to seeking to an offset. |
| 16 | // If the file is writable, operations that would write past the end of the offset file will expand | 16 | // If the file is writable, operations that would write past the end of the offset file will expand |
| 17 | // the size of this wrapper. | 17 | // the size of this wrapper. |
| 18 | struct OffsetVfsFile : public VfsFile { | 18 | class OffsetVfsFile : public VfsFile { |
| 19 | public: | ||
| 19 | OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, | 20 | OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, |
| 20 | std::string new_name = "", VirtualDir new_parent = nullptr); | 21 | std::string new_name = "", VirtualDir new_parent = nullptr); |
| 21 | 22 | ||
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index dc39c9f2f..179f62e4b 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h | |||
| @@ -10,7 +10,8 @@ namespace FileSys { | |||
| 10 | 10 | ||
| 11 | // An implementation of VfsDirectory that maintains two vectors for subdirectories and files. | 11 | // An implementation of VfsDirectory that maintains two vectors for subdirectories and files. |
| 12 | // Vector data is supplied upon construction. | 12 | // Vector data is supplied upon construction. |
| 13 | struct VectorVfsDirectory : public VfsDirectory { | 13 | class VectorVfsDirectory : public VfsDirectory { |
| 14 | public: | ||
| 14 | explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, | 15 | explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, |
| 15 | std::vector<VirtualDir> dirs = {}, std::string name = "", | 16 | std::vector<VirtualDir> dirs = {}, std::string name = "", |
| 16 | VirtualDir parent = nullptr); | 17 | VirtualDir parent = nullptr); |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 384dc7822..7006a37b3 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -34,9 +34,9 @@ class EmuWindow { | |||
| 34 | public: | 34 | public: |
| 35 | /// Data structure to store emuwindow configuration | 35 | /// Data structure to store emuwindow configuration |
| 36 | struct WindowConfig { | 36 | struct WindowConfig { |
| 37 | bool fullscreen; | 37 | bool fullscreen = false; |
| 38 | int res_width; | 38 | int res_width = 0; |
| 39 | int res_height; | 39 | int res_height = 0; |
| 40 | std::pair<unsigned, unsigned> min_client_area_size; | 40 | std::pair<unsigned, unsigned> min_client_area_size; |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1b0cd0abf..8c19e86d3 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | namespace Kernel { | 12 | namespace Kernel { |
| 13 | 13 | ||
| 14 | unsigned int Object::next_object_id; | 14 | std::atomic<u32> Object::next_object_id{0}; |
| 15 | 15 | ||
| 16 | /// Initialize the kernel | 16 | /// Initialize the kernel |
| 17 | void Init() { | 17 | void Init() { |
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index 83df68dfd..526ac9cc3 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <string> | 8 | #include <string> |
| 8 | #include <utility> | 9 | #include <utility> |
| 9 | 10 | ||
| @@ -42,8 +43,8 @@ public: | |||
| 42 | virtual ~Object(); | 43 | virtual ~Object(); |
| 43 | 44 | ||
| 44 | /// Returns a unique identifier for the object. For debugging purposes only. | 45 | /// Returns a unique identifier for the object. For debugging purposes only. |
| 45 | unsigned int GetObjectId() const { | 46 | u32 GetObjectId() const { |
| 46 | return object_id; | 47 | return object_id.load(std::memory_order_relaxed); |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | virtual std::string GetTypeName() const { | 50 | virtual std::string GetTypeName() const { |
| @@ -61,23 +62,23 @@ public: | |||
| 61 | bool IsWaitable() const; | 62 | bool IsWaitable() const; |
| 62 | 63 | ||
| 63 | public: | 64 | public: |
| 64 | static unsigned int next_object_id; | 65 | static std::atomic<u32> next_object_id; |
| 65 | 66 | ||
| 66 | private: | 67 | private: |
| 67 | friend void intrusive_ptr_add_ref(Object*); | 68 | friend void intrusive_ptr_add_ref(Object*); |
| 68 | friend void intrusive_ptr_release(Object*); | 69 | friend void intrusive_ptr_release(Object*); |
| 69 | 70 | ||
| 70 | unsigned int ref_count = 0; | 71 | std::atomic<u32> ref_count{0}; |
| 71 | unsigned int object_id = next_object_id++; | 72 | std::atomic<u32> object_id{next_object_id++}; |
| 72 | }; | 73 | }; |
| 73 | 74 | ||
| 74 | // Special functions used by boost::instrusive_ptr to do automatic ref-counting | 75 | // Special functions used by boost::instrusive_ptr to do automatic ref-counting |
| 75 | inline void intrusive_ptr_add_ref(Object* object) { | 76 | inline void intrusive_ptr_add_ref(Object* object) { |
| 76 | ++object->ref_count; | 77 | object->ref_count.fetch_add(1, std::memory_order_relaxed); |
| 77 | } | 78 | } |
| 78 | 79 | ||
| 79 | inline void intrusive_ptr_release(Object* object) { | 80 | inline void intrusive_ptr_release(Object* object) { |
| 80 | if (--object->ref_count == 0) { | 81 | if (object->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) { |
| 81 | delete object; | 82 | delete object; |
| 82 | } | 83 | } |
| 83 | } | 84 | } |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 94065c736..e770b9103 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -25,7 +25,7 @@ Scheduler::~Scheduler() { | |||
| 25 | } | 25 | } |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | bool Scheduler::HaveReadyThreads() { | 28 | bool Scheduler::HaveReadyThreads() const { |
| 29 | std::lock_guard<std::mutex> lock(scheduler_mutex); | 29 | std::lock_guard<std::mutex> lock(scheduler_mutex); |
| 30 | return ready_queue.get_first() != nullptr; | 30 | return ready_queue.get_first() != nullptr; |
| 31 | } | 31 | } |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 1a4ee8f36..6a61ef64e 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -21,7 +21,7 @@ public: | |||
| 21 | ~Scheduler(); | 21 | ~Scheduler(); |
| 22 | 22 | ||
| 23 | /// Returns whether there are any threads that are ready to run. | 23 | /// Returns whether there are any threads that are ready to run. |
| 24 | bool HaveReadyThreads(); | 24 | bool HaveReadyThreads() const; |
| 25 | 25 | ||
| 26 | /// Reschedules to the next available thread (call after current thread is suspended) | 26 | /// Reschedules to the next available thread (call after current thread is suspended) |
| 27 | void Reschedule(); | 27 | void Reschedule(); |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index d09ca5992..51a1ec160 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -152,7 +152,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { | |||
| 152 | // Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the | 152 | // Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the |
| 153 | // end of the command such that only commands following this one are handled as domains | 153 | // end of the command such that only commands following this one are handled as domains |
| 154 | if (convert_to_domain) { | 154 | if (convert_to_domain) { |
| 155 | ASSERT_MSG(domain_request_handlers.empty(), "already a domain"); | 155 | ASSERT_MSG(IsSession(), "ServerSession is already a domain instance."); |
| 156 | domain_request_handlers = {hle_handler}; | 156 | domain_request_handlers = {hle_handler}; |
| 157 | convert_to_domain = false; | 157 | convert_to_domain = false; |
| 158 | } | 158 | } |
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 2bce54fee..1a88e66b9 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h | |||
| @@ -97,7 +97,12 @@ public: | |||
| 97 | 97 | ||
| 98 | /// Returns true if the session has been converted to a domain, otherwise False | 98 | /// Returns true if the session has been converted to a domain, otherwise False |
| 99 | bool IsDomain() const { | 99 | bool IsDomain() const { |
| 100 | return !domain_request_handlers.empty(); | 100 | return !IsSession(); |
| 101 | } | ||
| 102 | |||
| 103 | /// Returns true if this session has not been converted to a domain, otherwise false. | ||
| 104 | bool IsSession() const { | ||
| 105 | return domain_request_handlers.empty(); | ||
| 101 | } | 106 | } |
| 102 | 107 | ||
| 103 | /// Converts the session to a domain at the end of the current command | 108 | /// Converts the session to a domain at the end of the current command |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 5db2db687..6be5c474e 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -250,8 +250,11 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | |||
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | /// Break program execution | 252 | /// Break program execution |
| 253 | static void Break(u64 unk_0, u64 unk_1, u64 unk_2) { | 253 | static void Break(u64 reason, u64 info1, u64 info2) { |
| 254 | LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); | 254 | LOG_CRITICAL( |
| 255 | Debug_Emulated, | ||
| 256 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | ||
| 257 | reason, info1, info2); | ||
| 255 | ASSERT(false); | 258 | ASSERT(false); |
| 256 | } | 259 | } |
| 257 | 260 | ||
| @@ -532,7 +535,6 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 532 | CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread)); | 535 | CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread)); |
| 533 | *out_handle = thread->guest_handle; | 536 | *out_handle = thread->guest_handle; |
| 534 | 537 | ||
| 535 | Core::System::GetInstance().PrepareReschedule(); | ||
| 536 | Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); | 538 | Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); |
| 537 | 539 | ||
| 538 | LOG_TRACE(Kernel_SVC, | 540 | LOG_TRACE(Kernel_SVC, |
| @@ -706,8 +708,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 706 | Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | 708 | Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); |
| 707 | auto owner = g_handle_table.Get<Thread>(owner_handle); | 709 | auto owner = g_handle_table.Get<Thread>(owner_handle); |
| 708 | ASSERT(owner); | 710 | ASSERT(owner); |
| 709 | ASSERT(thread->status != ThreadStatus::Running); | 711 | ASSERT(thread->status == ThreadStatus::WaitMutex); |
| 710 | thread->status = ThreadStatus::WaitMutex; | ||
| 711 | thread->wakeup_callback = nullptr; | 712 | thread->wakeup_callback = nullptr; |
| 712 | 713 | ||
| 713 | owner->AddMutexWaiter(thread); | 714 | owner->AddMutexWaiter(thread); |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b9022feae..cf4f94822 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include "core/hle/kernel/object.h" | 23 | #include "core/hle/kernel/object.h" |
| 24 | #include "core/hle/kernel/process.h" | 24 | #include "core/hle/kernel/process.h" |
| 25 | #include "core/hle/kernel/thread.h" | 25 | #include "core/hle/kernel/thread.h" |
| 26 | #include "core/hle/lock.h" | ||
| 26 | #include "core/hle/result.h" | 27 | #include "core/hle/result.h" |
| 27 | #include "core/memory.h" | 28 | #include "core/memory.h" |
| 28 | 29 | ||
| @@ -104,6 +105,10 @@ void ExitCurrentThread() { | |||
| 104 | */ | 105 | */ |
| 105 | static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | 106 | static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { |
| 106 | const auto proper_handle = static_cast<Handle>(thread_handle); | 107 | const auto proper_handle = static_cast<Handle>(thread_handle); |
| 108 | |||
| 109 | // Lock the global kernel mutex when we enter the kernel HLE. | ||
| 110 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 111 | |||
| 107 | SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle); | 112 | SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle); |
| 108 | if (thread == nullptr) { | 113 | if (thread == nullptr) { |
| 109 | LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); | 114 | LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); |
| @@ -155,12 +160,14 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||
| 155 | if (nanoseconds == -1) | 160 | if (nanoseconds == -1) |
| 156 | return; | 161 | return; |
| 157 | 162 | ||
| 158 | CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(nanoseconds), ThreadWakeupEventType, | 163 | // This function might be called from any thread so we have to be cautious and use the |
| 159 | callback_handle); | 164 | // thread-safe version of ScheduleEvent. |
| 165 | CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds), ThreadWakeupEventType, | ||
| 166 | callback_handle); | ||
| 160 | } | 167 | } |
| 161 | 168 | ||
| 162 | void Thread::CancelWakeupTimer() { | 169 | void Thread::CancelWakeupTimer() { |
| 163 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | 170 | CoreTiming::UnscheduleEventThreadsafe(ThreadWakeupEventType, callback_handle); |
| 164 | } | 171 | } |
| 165 | 172 | ||
| 166 | static boost::optional<s32> GetNextProcessorId(u64 mask) { | 173 | static boost::optional<s32> GetNextProcessorId(u64 mask) { |
| @@ -419,12 +426,33 @@ VAddr Thread::GetCommandBufferAddress() const { | |||
| 419 | } | 426 | } |
| 420 | 427 | ||
| 421 | void Thread::AddMutexWaiter(SharedPtr<Thread> thread) { | 428 | void Thread::AddMutexWaiter(SharedPtr<Thread> thread) { |
| 429 | if (thread->lock_owner == this) { | ||
| 430 | // If the thread is already waiting for this thread to release the mutex, ensure that the | ||
| 431 | // waiters list is consistent and return without doing anything. | ||
| 432 | auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); | ||
| 433 | ASSERT(itr != wait_mutex_threads.end()); | ||
| 434 | return; | ||
| 435 | } | ||
| 436 | |||
| 437 | // A thread can't wait on two different mutexes at the same time. | ||
| 438 | ASSERT(thread->lock_owner == nullptr); | ||
| 439 | |||
| 440 | // Ensure that the thread is not already in the list of mutex waiters | ||
| 441 | auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); | ||
| 442 | ASSERT(itr == wait_mutex_threads.end()); | ||
| 443 | |||
| 422 | thread->lock_owner = this; | 444 | thread->lock_owner = this; |
| 423 | wait_mutex_threads.emplace_back(std::move(thread)); | 445 | wait_mutex_threads.emplace_back(std::move(thread)); |
| 424 | UpdatePriority(); | 446 | UpdatePriority(); |
| 425 | } | 447 | } |
| 426 | 448 | ||
| 427 | void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) { | 449 | void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) { |
| 450 | ASSERT(thread->lock_owner == this); | ||
| 451 | |||
| 452 | // Ensure that the thread is in the list of mutex waiters | ||
| 453 | auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); | ||
| 454 | ASSERT(itr != wait_mutex_threads.end()); | ||
| 455 | |||
| 428 | boost::remove_erase(wait_mutex_threads, thread); | 456 | boost::remove_erase(wait_mutex_threads, thread); |
| 429 | thread->lock_owner = nullptr; | 457 | thread->lock_owner = nullptr; |
| 430 | UpdatePriority(); | 458 | UpdatePriority(); |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 108a7c6eb..ce709ccf4 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -28,7 +28,7 @@ constexpr int DefaultSampleRate{48000}; | |||
| 28 | class IAudioOut final : public ServiceFramework<IAudioOut> { | 28 | class IAudioOut final : public ServiceFramework<IAudioOut> { |
| 29 | public: | 29 | public: |
| 30 | IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) | 30 | IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) |
| 31 | : ServiceFramework("IAudioOut"), audio_params(audio_params), audio_core(audio_core) { | 31 | : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params) { |
| 32 | 32 | ||
| 33 | static const FunctionInfo functions[] = { | 33 | static const FunctionInfo functions[] = { |
| 34 | {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, | 34 | {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index f99304de5..9e75eb3a6 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -20,9 +20,9 @@ public: | |||
| 20 | explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) | 20 | explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) |
| 21 | : ServiceFramework("IAudioRenderer") { | 21 | : ServiceFramework("IAudioRenderer") { |
| 22 | static const FunctionInfo functions[] = { | 22 | static const FunctionInfo functions[] = { |
| 23 | {0, nullptr, "GetAudioRendererSampleRate"}, | 23 | {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"}, |
| 24 | {1, nullptr, "GetAudioRendererSampleCount"}, | 24 | {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"}, |
| 25 | {2, nullptr, "GetAudioRendererMixBufferCount"}, | 25 | {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"}, |
| 26 | {3, nullptr, "GetAudioRendererState"}, | 26 | {3, nullptr, "GetAudioRendererState"}, |
| 27 | {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, | 27 | {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, |
| 28 | {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, | 28 | {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, |
| @@ -45,6 +45,27 @@ private: | |||
| 45 | system_event->Signal(); | 45 | system_event->Signal(); |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | void GetAudioRendererSampleRate(Kernel::HLERequestContext& ctx) { | ||
| 49 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 50 | rb.Push(RESULT_SUCCESS); | ||
| 51 | rb.Push<u32>(renderer->GetSampleRate()); | ||
| 52 | LOG_DEBUG(Service_Audio, "called"); | ||
| 53 | } | ||
| 54 | |||
| 55 | void GetAudioRendererSampleCount(Kernel::HLERequestContext& ctx) { | ||
| 56 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 57 | rb.Push(RESULT_SUCCESS); | ||
| 58 | rb.Push<u32>(renderer->GetSampleCount()); | ||
| 59 | LOG_DEBUG(Service_Audio, "called"); | ||
| 60 | } | ||
| 61 | |||
| 62 | void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) { | ||
| 63 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 64 | rb.Push(RESULT_SUCCESS); | ||
| 65 | rb.Push<u32>(renderer->GetMixBufferCount()); | ||
| 66 | LOG_DEBUG(Service_Audio, "called"); | ||
| 67 | } | ||
| 68 | |||
| 48 | void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { | 69 | void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { |
| 49 | ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); | 70 | ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); |
| 50 | IPC::ResponseBuilder rb{ctx, 2}; | 71 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -169,7 +190,8 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") { | |||
| 169 | {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"}, | 190 | {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"}, |
| 170 | {2, &AudRenU::GetAudioDevice, "GetAudioDevice"}, | 191 | {2, &AudRenU::GetAudioDevice, "GetAudioDevice"}, |
| 171 | {3, nullptr, "OpenAudioRendererAuto"}, | 192 | {3, nullptr, "OpenAudioRendererAuto"}, |
| 172 | {4, nullptr, "GetAudioDeviceServiceWithRevisionInfo"}, | 193 | {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, |
| 194 | "GetAudioDeviceServiceWithRevisionInfo"}, | ||
| 173 | }; | 195 | }; |
| 174 | RegisterHandlers(functions); | 196 | RegisterHandlers(functions); |
| 175 | } | 197 | } |
| @@ -189,7 +211,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 189 | IPC::RequestParser rp{ctx}; | 211 | IPC::RequestParser rp{ctx}; |
| 190 | auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); | 212 | auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); |
| 191 | 213 | ||
| 192 | u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40); | 214 | u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40); |
| 193 | buffer_sz += params.unknown_c * 1024; | 215 | buffer_sz += params.unknown_c * 1024; |
| 194 | buffer_sz += 0x940 * (params.unknown_c + 1); | 216 | buffer_sz += 0x940 * (params.unknown_c + 1); |
| 195 | buffer_sz += 0x3F0 * params.voice_count; | 217 | buffer_sz += 0x3F0 * params.voice_count; |
| @@ -197,7 +219,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 197 | buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10); | 219 | buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10); |
| 198 | buffer_sz += | 220 | buffer_sz += |
| 199 | Common::AlignUp((0x3C0 * (params.sink_count + params.unknown_c) + 4 * params.sample_count) * | 221 | Common::AlignUp((0x3C0 * (params.sink_count + params.unknown_c) + 4 * params.sample_count) * |
| 200 | (params.unknown_8 + 6), | 222 | (params.mix_buffer_count + 6), |
| 201 | 0x40); | 223 | 0x40); |
| 202 | 224 | ||
| 203 | if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { | 225 | if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { |
| @@ -253,6 +275,16 @@ void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) { | |||
| 253 | LOG_DEBUG(Service_Audio, "called"); | 275 | LOG_DEBUG(Service_Audio, "called"); |
| 254 | } | 276 | } |
| 255 | 277 | ||
| 278 | void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { | ||
| 279 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 280 | |||
| 281 | rb.Push(RESULT_SUCCESS); | ||
| 282 | rb.PushIpcInterface<Audio::IAudioDevice>(); | ||
| 283 | |||
| 284 | LOG_WARNING(Service_Audio, "(STUBBED) called"); // TODO(ogniK): Figure out what is different | ||
| 285 | // based on the current revision | ||
| 286 | } | ||
| 287 | |||
| 256 | bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { | 288 | bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { |
| 257 | u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap | 289 | u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap |
| 258 | switch (feature) { | 290 | switch (feature) { |
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 14907f8ae..8600ac6e4 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h | |||
| @@ -22,6 +22,7 @@ private: | |||
| 22 | void OpenAudioRenderer(Kernel::HLERequestContext& ctx); | 22 | void OpenAudioRenderer(Kernel::HLERequestContext& ctx); |
| 23 | void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); | 23 | void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); |
| 24 | void GetAudioDevice(Kernel::HLERequestContext& ctx); | 24 | void GetAudioDevice(Kernel::HLERequestContext& ctx); |
| 25 | void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx); | ||
| 25 | 26 | ||
| 26 | enum class AudioFeatures : u32 { | 27 | enum class AudioFeatures : u32 { |
| 27 | Splitter, | 28 | Splitter, |
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 2b642c32f..f2b0e509a 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -26,7 +26,7 @@ public: | |||
| 26 | {10600, nullptr, "DeclareOpenOnlinePlaySession"}, | 26 | {10600, nullptr, "DeclareOpenOnlinePlaySession"}, |
| 27 | {10601, &IFriendService::DeclareCloseOnlinePlaySession, | 27 | {10601, &IFriendService::DeclareCloseOnlinePlaySession, |
| 28 | "DeclareCloseOnlinePlaySession"}, | 28 | "DeclareCloseOnlinePlaySession"}, |
| 29 | {10610, nullptr, "UpdateUserPresence"}, | 29 | {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"}, |
| 30 | {10700, nullptr, "GetPlayHistoryRegistrationKey"}, | 30 | {10700, nullptr, "GetPlayHistoryRegistrationKey"}, |
| 31 | {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"}, | 31 | {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"}, |
| 32 | {10702, nullptr, "AddPlayHistory"}, | 32 | {10702, nullptr, "AddPlayHistory"}, |
| @@ -99,6 +99,13 @@ private: | |||
| 99 | IPC::ResponseBuilder rb{ctx, 2}; | 99 | IPC::ResponseBuilder rb{ctx, 2}; |
| 100 | rb.Push(RESULT_SUCCESS); | 100 | rb.Push(RESULT_SUCCESS); |
| 101 | } | 101 | } |
| 102 | |||
| 103 | void UpdateUserPresence(Kernel::HLERequestContext& ctx) { | ||
| 104 | // Stub used by Retro City Rampage | ||
| 105 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 106 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 107 | rb.Push(RESULT_SUCCESS); | ||
| 108 | } | ||
| 102 | }; | 109 | }; |
| 103 | 110 | ||
| 104 | void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { | 111 | void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index dcdfa0e19..970942d3f 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -291,6 +291,7 @@ private: | |||
| 291 | class Hid final : public ServiceFramework<Hid> { | 291 | class Hid final : public ServiceFramework<Hid> { |
| 292 | public: | 292 | public: |
| 293 | Hid() : ServiceFramework("hid") { | 293 | Hid() : ServiceFramework("hid") { |
| 294 | // clang-format off | ||
| 294 | static const FunctionInfo functions[] = { | 295 | static const FunctionInfo functions[] = { |
| 295 | {0, &Hid::CreateAppletResource, "CreateAppletResource"}, | 296 | {0, &Hid::CreateAppletResource, "CreateAppletResource"}, |
| 296 | {1, &Hid::ActivateDebugPad, "ActivateDebugPad"}, | 297 | {1, &Hid::ActivateDebugPad, "ActivateDebugPad"}, |
| @@ -333,15 +334,13 @@ public: | |||
| 333 | {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, | 334 | {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, |
| 334 | {103, &Hid::ActivateNpad, "ActivateNpad"}, | 335 | {103, &Hid::ActivateNpad, "ActivateNpad"}, |
| 335 | {104, nullptr, "DeactivateNpad"}, | 336 | {104, nullptr, "DeactivateNpad"}, |
| 336 | {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, | 337 | {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, |
| 337 | "AcquireNpadStyleSetUpdateEventHandle"}, | 338 | {107, &Hid::DisconnectNpad, "DisconnectNpad"}, |
| 338 | {107, nullptr, "DisconnectNpad"}, | ||
| 339 | {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, | 339 | {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, |
| 340 | {109, nullptr, "ActivateNpadWithRevision"}, | 340 | {109, nullptr, "ActivateNpadWithRevision"}, |
| 341 | {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, | 341 | {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, |
| 342 | {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, | 342 | {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, |
| 343 | {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, | 343 | {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, |
| 344 | "SetNpadJoyAssignmentModeSingleByDefault"}, | ||
| 345 | {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"}, | 344 | {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"}, |
| 346 | {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, | 345 | {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, |
| 347 | {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, | 346 | {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, |
| @@ -398,6 +397,8 @@ public: | |||
| 398 | {1000, nullptr, "SetNpadCommunicationMode"}, | 397 | {1000, nullptr, "SetNpadCommunicationMode"}, |
| 399 | {1001, nullptr, "GetNpadCommunicationMode"}, | 398 | {1001, nullptr, "GetNpadCommunicationMode"}, |
| 400 | }; | 399 | }; |
| 400 | // clang-format on | ||
| 401 | |||
| 401 | RegisterHandlers(functions); | 402 | RegisterHandlers(functions); |
| 402 | 403 | ||
| 403 | event = Kernel::Event::Create(Kernel::ResetType::OneShot, "hid:EventHandle"); | 404 | event = Kernel::Event::Create(Kernel::ResetType::OneShot, "hid:EventHandle"); |
| @@ -496,6 +497,12 @@ private: | |||
| 496 | LOG_WARNING(Service_HID, "(STUBBED) called"); | 497 | LOG_WARNING(Service_HID, "(STUBBED) called"); |
| 497 | } | 498 | } |
| 498 | 499 | ||
| 500 | void DisconnectNpad(Kernel::HLERequestContext& ctx) { | ||
| 501 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 502 | rb.Push(RESULT_SUCCESS); | ||
| 503 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 504 | } | ||
| 505 | |||
| 499 | void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { | 506 | void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { |
| 500 | IPC::ResponseBuilder rb{ctx, 2}; | 507 | IPC::ResponseBuilder rb{ctx, 2}; |
| 501 | rb.Push(RESULT_SUCCESS); | 508 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 2e99ddf51..098da2a41 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp | |||
| @@ -92,7 +92,11 @@ private: | |||
| 92 | 92 | ||
| 93 | // Parse out log metadata | 93 | // Parse out log metadata |
| 94 | u32 line{}; | 94 | u32 line{}; |
| 95 | std::string message, filename, function; | 95 | std::string module; |
| 96 | std::string message; | ||
| 97 | std::string filename; | ||
| 98 | std::string function; | ||
| 99 | std::string thread; | ||
| 96 | while (addr < end_addr) { | 100 | while (addr < end_addr) { |
| 97 | const Field field{static_cast<Field>(Memory::Read8(addr++))}; | 101 | const Field field{static_cast<Field>(Memory::Read8(addr++))}; |
| 98 | const size_t length{Memory::Read8(addr++)}; | 102 | const size_t length{Memory::Read8(addr++)}; |
| @@ -102,6 +106,8 @@ private: | |||
| 102 | } | 106 | } |
| 103 | 107 | ||
| 104 | switch (field) { | 108 | switch (field) { |
| 109 | case Field::Skip: | ||
| 110 | break; | ||
| 105 | case Field::Message: | 111 | case Field::Message: |
| 106 | message = Memory::ReadCString(addr, length); | 112 | message = Memory::ReadCString(addr, length); |
| 107 | break; | 113 | break; |
| @@ -114,6 +120,12 @@ private: | |||
| 114 | case Field::Function: | 120 | case Field::Function: |
| 115 | function = Memory::ReadCString(addr, length); | 121 | function = Memory::ReadCString(addr, length); |
| 116 | break; | 122 | break; |
| 123 | case Field::Module: | ||
| 124 | module = Memory::ReadCString(addr, length); | ||
| 125 | break; | ||
| 126 | case Field::Thread: | ||
| 127 | thread = Memory::ReadCString(addr, length); | ||
| 128 | break; | ||
| 117 | } | 129 | } |
| 118 | 130 | ||
| 119 | addr += length; | 131 | addr += length; |
| @@ -128,12 +140,18 @@ private: | |||
| 128 | if (!filename.empty()) { | 140 | if (!filename.empty()) { |
| 129 | log_stream << filename << ':'; | 141 | log_stream << filename << ':'; |
| 130 | } | 142 | } |
| 143 | if (!module.empty()) { | ||
| 144 | log_stream << module << ':'; | ||
| 145 | } | ||
| 131 | if (!function.empty()) { | 146 | if (!function.empty()) { |
| 132 | log_stream << function << ':'; | 147 | log_stream << function << ':'; |
| 133 | } | 148 | } |
| 134 | if (line) { | 149 | if (line) { |
| 135 | log_stream << std::to_string(line) << ':'; | 150 | log_stream << std::to_string(line) << ':'; |
| 136 | } | 151 | } |
| 152 | if (!thread.empty()) { | ||
| 153 | log_stream << thread << ':'; | ||
| 154 | } | ||
| 137 | if (log_stream.str().length() > 0 && log_stream.str().back() == ':') { | 155 | if (log_stream.str().length() > 0 && log_stream.str().back() == ':') { |
| 138 | log_stream << ' '; | 156 | log_stream << ' '; |
| 139 | } | 157 | } |
| @@ -142,7 +160,7 @@ private: | |||
| 142 | if (header.IsTailLog()) { | 160 | if (header.IsTailLog()) { |
| 143 | switch (header.severity) { | 161 | switch (header.severity) { |
| 144 | case MessageHeader::Severity::Trace: | 162 | case MessageHeader::Severity::Trace: |
| 145 | LOG_TRACE(Debug_Emulated, "{}", log_stream.str()); | 163 | LOG_DEBUG(Debug_Emulated, "{}", log_stream.str()); |
| 146 | break; | 164 | break; |
| 147 | case MessageHeader::Severity::Info: | 165 | case MessageHeader::Severity::Info: |
| 148 | LOG_INFO(Debug_Emulated, "{}", log_stream.str()); | 166 | LOG_INFO(Debug_Emulated, "{}", log_stream.str()); |
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index 08f45b78a..7b91bb258 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp | |||
| @@ -9,42 +9,63 @@ | |||
| 9 | 9 | ||
| 10 | namespace Service::MM { | 10 | namespace Service::MM { |
| 11 | 11 | ||
| 12 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 12 | class MM_U final : public ServiceFramework<MM_U> { |
| 13 | std::make_shared<MM_U>()->InstallAsService(service_manager); | 13 | public: |
| 14 | } | 14 | explicit MM_U() : ServiceFramework{"mm:u"} { |
| 15 | // clang-format off | ||
| 16 | static const FunctionInfo functions[] = { | ||
| 17 | {0, &MM_U::Initialize, "InitializeOld"}, | ||
| 18 | {1, &MM_U::Finalize, "FinalizeOld"}, | ||
| 19 | {2, &MM_U::SetAndWait, "SetAndWaitOld"}, | ||
| 20 | {3, &MM_U::Get, "GetOld"}, | ||
| 21 | {4, &MM_U::Initialize, "Initialize"}, | ||
| 22 | {5, &MM_U::Finalize, "Finalize"}, | ||
| 23 | {6, &MM_U::SetAndWait, "SetAndWait"}, | ||
| 24 | {7, &MM_U::Get, "Get"}, | ||
| 25 | }; | ||
| 26 | // clang-format on | ||
| 15 | 27 | ||
| 16 | void MM_U::Initialize(Kernel::HLERequestContext& ctx) { | 28 | RegisterHandlers(functions); |
| 17 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 29 | } |
| 18 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 19 | rb.Push(RESULT_SUCCESS); | ||
| 20 | } | ||
| 21 | 30 | ||
| 22 | void MM_U::SetAndWait(Kernel::HLERequestContext& ctx) { | 31 | private: |
| 23 | IPC::RequestParser rp{ctx}; | 32 | void Initialize(Kernel::HLERequestContext& ctx) { |
| 24 | min = rp.Pop<u32>(); | 33 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 25 | max = rp.Pop<u32>(); | 34 | IPC::ResponseBuilder rb{ctx, 2}; |
| 26 | current = min; | 35 | rb.Push(RESULT_SUCCESS); |
| 36 | } | ||
| 27 | 37 | ||
| 28 | LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); | 38 | void Finalize(Kernel::HLERequestContext& ctx) { |
| 29 | IPC::ResponseBuilder rb{ctx, 2}; | 39 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 30 | rb.Push(RESULT_SUCCESS); | 40 | IPC::ResponseBuilder rb{ctx, 2}; |
| 31 | } | 41 | rb.Push(RESULT_SUCCESS); |
| 42 | } | ||
| 32 | 43 | ||
| 33 | void MM_U::Get(Kernel::HLERequestContext& ctx) { | 44 | void SetAndWait(Kernel::HLERequestContext& ctx) { |
| 34 | LOG_WARNING(Service_MM, "(STUBBED) called"); | 45 | IPC::RequestParser rp{ctx}; |
| 35 | IPC::ResponseBuilder rb{ctx, 3}; | 46 | min = rp.Pop<u32>(); |
| 36 | rb.Push(RESULT_SUCCESS); | 47 | max = rp.Pop<u32>(); |
| 37 | rb.Push(current); | 48 | current = min; |
| 38 | } | ||
| 39 | 49 | ||
| 40 | MM_U::MM_U() : ServiceFramework("mm:u") { | 50 | LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); |
| 41 | static const FunctionInfo functions[] = { | 51 | IPC::ResponseBuilder rb{ctx, 2}; |
| 42 | {0, nullptr, "InitializeOld"}, {1, nullptr, "FinalizeOld"}, | 52 | rb.Push(RESULT_SUCCESS); |
| 43 | {2, nullptr, "SetAndWaitOld"}, {3, nullptr, "GetOld"}, | 53 | } |
| 44 | {4, &MM_U::Initialize, "Initialize"}, {5, nullptr, "Finalize"}, | 54 | |
| 45 | {6, &MM_U::SetAndWait, "SetAndWait"}, {7, &MM_U::Get, "Get"}, | 55 | void Get(Kernel::HLERequestContext& ctx) { |
| 46 | }; | 56 | LOG_WARNING(Service_MM, "(STUBBED) called"); |
| 47 | RegisterHandlers(functions); | 57 | IPC::ResponseBuilder rb{ctx, 3}; |
| 58 | rb.Push(RESULT_SUCCESS); | ||
| 59 | rb.Push(current); | ||
| 60 | } | ||
| 61 | |||
| 62 | u32 min{0}; | ||
| 63 | u32 max{0}; | ||
| 64 | u32 current{0}; | ||
| 65 | }; | ||
| 66 | |||
| 67 | void InstallInterfaces(SM::ServiceManager& service_manager) { | ||
| 68 | std::make_shared<MM_U>()->InstallAsService(service_manager); | ||
| 48 | } | 69 | } |
| 49 | 70 | ||
| 50 | } // namespace Service::MM | 71 | } // namespace Service::MM |
diff --git a/src/core/hle/service/mm/mm_u.h b/src/core/hle/service/mm/mm_u.h index 79eeedf9c..5439fa653 100644 --- a/src/core/hle/service/mm/mm_u.h +++ b/src/core/hle/service/mm/mm_u.h | |||
| @@ -8,21 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | namespace Service::MM { | 9 | namespace Service::MM { |
| 10 | 10 | ||
| 11 | class MM_U final : public ServiceFramework<MM_U> { | ||
| 12 | public: | ||
| 13 | MM_U(); | ||
| 14 | ~MM_U() = default; | ||
| 15 | |||
| 16 | private: | ||
| 17 | void Initialize(Kernel::HLERequestContext& ctx); | ||
| 18 | void SetAndWait(Kernel::HLERequestContext& ctx); | ||
| 19 | void Get(Kernel::HLERequestContext& ctx); | ||
| 20 | |||
| 21 | u32 min{0}; | ||
| 22 | u32 max{0}; | ||
| 23 | u32 current{0}; | ||
| 24 | }; | ||
| 25 | |||
| 26 | /// Registers all MM services with the specified service manager. | 11 | /// Registers all MM services with the specified service manager. |
| 27 | void InstallInterfaces(SM::ServiceManager& service_manager); | 12 | void InstallInterfaces(SM::ServiceManager& service_manager); |
| 28 | 13 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp new file mode 100644 index 000000000..51f01077b --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" | ||
| 10 | |||
| 11 | namespace Service::Nvidia::Devices { | ||
| 12 | |||
| 13 | u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 14 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | ||
| 15 | command.raw, input.size(), output.size()); | ||
| 16 | |||
| 17 | switch (static_cast<IoctlCommand>(command.raw)) { | ||
| 18 | case IoctlCommand::IocSetNVMAPfdCommand: | ||
| 19 | return SetNVMAPfd(input, output); | ||
| 20 | } | ||
| 21 | |||
| 22 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | ||
| 23 | return 0; | ||
| 24 | } | ||
| 25 | |||
| 26 | u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 27 | IoctlSetNvmapFD params{}; | ||
| 28 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 29 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); | ||
| 30 | nvmap_fd = params.nvmap_fd; | ||
| 31 | return 0; | ||
| 32 | } | ||
| 33 | |||
| 34 | } // namespace Service::Nvidia::Devices | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h new file mode 100644 index 000000000..2b0eb43ee --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/swap.h" | ||
| 10 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | ||
| 11 | |||
| 12 | namespace Service::Nvidia::Devices { | ||
| 13 | |||
| 14 | class nvhost_nvjpg final : public nvdevice { | ||
| 15 | public: | ||
| 16 | nvhost_nvjpg() = default; | ||
| 17 | ~nvhost_nvjpg() override = default; | ||
| 18 | |||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | ||
| 20 | |||
| 21 | private: | ||
| 22 | enum class IoctlCommand : u32_le { | ||
| 23 | IocSetNVMAPfdCommand = 0x40044801, | ||
| 24 | }; | ||
| 25 | |||
| 26 | struct IoctlSetNvmapFD { | ||
| 27 | u32_le nvmap_fd; | ||
| 28 | }; | ||
| 29 | static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); | ||
| 30 | |||
| 31 | u32_le nvmap_fd{}; | ||
| 32 | |||
| 33 | u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 34 | }; | ||
| 35 | |||
| 36 | } // namespace Service::Nvidia::Devices | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp new file mode 100644 index 000000000..fcb488d50 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/hle/service/nvdrv/devices/nvhost_vic.h" | ||
| 10 | |||
| 11 | namespace Service::Nvidia::Devices { | ||
| 12 | |||
| 13 | u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 14 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | ||
| 15 | command.raw, input.size(), output.size()); | ||
| 16 | |||
| 17 | switch (static_cast<IoctlCommand>(command.raw)) { | ||
| 18 | case IoctlCommand::IocSetNVMAPfdCommand: | ||
| 19 | return SetNVMAPfd(input, output); | ||
| 20 | } | ||
| 21 | |||
| 22 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | ||
| 23 | return 0; | ||
| 24 | } | ||
| 25 | |||
| 26 | u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 27 | IoctlSetNvmapFD params{}; | ||
| 28 | std::memcpy(¶ms, input.data(), input.size()); | ||
| 29 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); | ||
| 30 | nvmap_fd = params.nvmap_fd; | ||
| 31 | return 0; | ||
| 32 | } | ||
| 33 | |||
| 34 | } // namespace Service::Nvidia::Devices | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h new file mode 100644 index 000000000..c7d681e52 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/swap.h" | ||
| 10 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | ||
| 11 | |||
| 12 | namespace Service::Nvidia::Devices { | ||
| 13 | |||
| 14 | class nvhost_vic final : public nvdevice { | ||
| 15 | public: | ||
| 16 | nvhost_vic() = default; | ||
| 17 | ~nvhost_vic() override = default; | ||
| 18 | |||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | ||
| 20 | |||
| 21 | private: | ||
| 22 | enum class IoctlCommand : u32_le { | ||
| 23 | IocSetNVMAPfdCommand = 0x40044801, | ||
| 24 | }; | ||
| 25 | |||
| 26 | struct IoctlSetNvmapFD { | ||
| 27 | u32_le nvmap_fd; | ||
| 28 | }; | ||
| 29 | static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); | ||
| 30 | |||
| 31 | u32_le nvmap_fd{}; | ||
| 32 | |||
| 33 | u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 34 | }; | ||
| 35 | |||
| 36 | } // namespace Service::Nvidia::Devices | ||
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 427f4b574..2de39822f 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp | |||
| @@ -12,6 +12,8 @@ | |||
| 12 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" | 12 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" |
| 13 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" | 13 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" |
| 14 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" | 14 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" |
| 15 | #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" | ||
| 16 | #include "core/hle/service/nvdrv/devices/nvhost_vic.h" | ||
| 15 | #include "core/hle/service/nvdrv/devices/nvmap.h" | 17 | #include "core/hle/service/nvdrv/devices/nvmap.h" |
| 16 | #include "core/hle/service/nvdrv/interface.h" | 18 | #include "core/hle/service/nvdrv/interface.h" |
| 17 | #include "core/hle/service/nvdrv/nvdrv.h" | 19 | #include "core/hle/service/nvdrv/nvdrv.h" |
| @@ -39,6 +41,8 @@ Module::Module() { | |||
| 39 | devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev); | 41 | devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev); |
| 40 | devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(); | 42 | devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(); |
| 41 | devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(); | 43 | devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(); |
| 44 | devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(); | ||
| 45 | devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(); | ||
| 42 | } | 46 | } |
| 43 | 47 | ||
| 44 | u32 Module::Open(const std::string& device_name) { | 48 | u32 Module::Open(const std::string& device_name) { |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 8a294c0f2..cd9c74f3d 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -23,7 +23,7 @@ class HLERequestContext; | |||
| 23 | } // namespace Kernel | 23 | } // namespace Kernel |
| 24 | 24 | ||
| 25 | namespace FileSys { | 25 | namespace FileSys { |
| 26 | struct VfsFilesystem; | 26 | class VfsFilesystem; |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | namespace Service { | 29 | namespace Service { |
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index 518a0cc46..1cef73216 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | namespace Service::SM { | 10 | namespace Service::SM { |
| 11 | 11 | ||
| 12 | void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) { | 12 | void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) { |
| 13 | ASSERT_MSG(!ctx.Session()->IsDomain(), "session is alread a domain"); | 13 | ASSERT_MSG(ctx.Session()->IsSession(), "Session is already a domain"); |
| 14 | ctx.Session()->ConvertToDomain(); | 14 | ctx.Session()->ConvertToDomain(); |
| 15 | 15 | ||
| 16 | IPC::ResponseBuilder rb{ctx, 3}; | 16 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -41,7 +41,7 @@ void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) { | |||
| 41 | void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { | 41 | void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { |
| 42 | IPC::ResponseBuilder rb{ctx, 3}; | 42 | IPC::ResponseBuilder rb{ctx, 3}; |
| 43 | rb.Push(RESULT_SUCCESS); | 43 | rb.Push(RESULT_SUCCESS); |
| 44 | rb.Push<u32>(0x500); | 44 | rb.Push<u16>(0x500); |
| 45 | 45 | ||
| 46 | LOG_WARNING(Service, "(STUBBED) called"); | 46 | LOG_WARNING(Service, "(STUBBED) called"); |
| 47 | } | 47 | } |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index de05f21d8..d575a9bea 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -118,7 +118,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 118 | 118 | ||
| 119 | process->program_id = metadata.GetTitleID(); | 119 | process->program_id = metadata.GetTitleID(); |
| 120 | process->svc_access_mask.set(); | 120 | process->svc_access_mask.set(); |
| 121 | process->address_mappings = default_address_mappings; | ||
| 122 | process->resource_limit = | 121 | process->resource_limit = |
| 123 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | 122 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); |
| 124 | process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), | 123 | process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 401cad3ab..6420a7f11 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -398,7 +398,6 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 398 | 398 | ||
| 399 | process->LoadModule(codeset, codeset->entrypoint); | 399 | process->LoadModule(codeset, codeset->entrypoint); |
| 400 | process->svc_access_mask.set(); | 400 | process->svc_access_mask.set(); |
| 401 | process->address_mappings = default_address_mappings; | ||
| 402 | 401 | ||
| 403 | // Attach the default resource limit (APPLICATION) to the process | 402 | // Attach the default resource limit (APPLICATION) to the process |
| 404 | process->resource_limit = | 403 | process->resource_limit = |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 0e690abb3..70ef5d240 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <ostream> | ||
| 6 | #include <string> | 7 | #include <string> |
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 8 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| @@ -17,12 +18,6 @@ | |||
| 17 | 18 | ||
| 18 | namespace Loader { | 19 | namespace Loader { |
| 19 | 20 | ||
| 20 | const std::initializer_list<Kernel::AddressMapping> default_address_mappings = { | ||
| 21 | {0x1FF50000, 0x8000, true}, // part of DSP RAM | ||
| 22 | {0x1FF70000, 0x8000, true}, // part of DSP RAM | ||
| 23 | {0x1F000000, 0x600000, false}, // entire VRAM | ||
| 24 | }; | ||
| 25 | |||
| 26 | FileType IdentifyFile(FileSys::VirtualFile file) { | 21 | FileType IdentifyFile(FileSys::VirtualFile file) { |
| 27 | FileType type; | 22 | FileType type; |
| 28 | 23 | ||
| @@ -127,14 +122,9 @@ constexpr std::array<const char*, 36> RESULT_MESSAGES{ | |||
| 127 | "There is no control data available.", | 122 | "There is no control data available.", |
| 128 | }; | 123 | }; |
| 129 | 124 | ||
| 130 | std::string GetMessageForResultStatus(ResultStatus status) { | 125 | std::ostream& operator<<(std::ostream& os, ResultStatus status) { |
| 131 | return GetMessageForResultStatus(static_cast<size_t>(status)); | 126 | os << RESULT_MESSAGES.at(static_cast<size_t>(status)); |
| 132 | } | 127 | return os; |
| 133 | |||
| 134 | std::string GetMessageForResultStatus(u16 status) { | ||
| 135 | if (status >= 36) | ||
| 136 | return ""; | ||
| 137 | return RESULT_MESSAGES[status]; | ||
| 138 | } | 128 | } |
| 139 | 129 | ||
| 140 | /** | 130 | /** |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index cfdadbee3..b74cfbf8a 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | 7 | #include <algorithm> |
| 8 | #include <initializer_list> | 8 | #include <iosfwd> |
| 9 | #include <memory> | 9 | #include <memory> |
| 10 | #include <string> | 10 | #include <string> |
| 11 | #include <utility> | 11 | #include <utility> |
| @@ -56,7 +56,7 @@ FileType GuessFromFilename(const std::string& name); | |||
| 56 | std::string GetFileTypeString(FileType type); | 56 | std::string GetFileTypeString(FileType type); |
| 57 | 57 | ||
| 58 | /// Return type for functions in Loader namespace | 58 | /// Return type for functions in Loader namespace |
| 59 | enum class ResultStatus { | 59 | enum class ResultStatus : u16 { |
| 60 | Success, | 60 | Success, |
| 61 | ErrorAlreadyLoaded, | 61 | ErrorAlreadyLoaded, |
| 62 | ErrorNotImplemented, | 62 | ErrorNotImplemented, |
| @@ -95,8 +95,7 @@ enum class ResultStatus { | |||
| 95 | ErrorNoControl, | 95 | ErrorNoControl, |
| 96 | }; | 96 | }; |
| 97 | 97 | ||
| 98 | std::string GetMessageForResultStatus(ResultStatus status); | 98 | std::ostream& operator<<(std::ostream& os, ResultStatus status); |
| 99 | std::string GetMessageForResultStatus(u16 status); | ||
| 100 | 99 | ||
| 101 | /// Interface for loading an application | 100 | /// Interface for loading an application |
| 102 | class AppLoader : NonCopyable { | 101 | class AppLoader : NonCopyable { |
| @@ -208,12 +207,6 @@ protected: | |||
| 208 | }; | 207 | }; |
| 209 | 208 | ||
| 210 | /** | 209 | /** |
| 211 | * Common address mappings found in most games, used for binary formats that don't have this | ||
| 212 | * information. | ||
| 213 | */ | ||
| 214 | extern const std::initializer_list<Kernel::AddressMapping> default_address_mappings; | ||
| 215 | |||
| 216 | /** | ||
| 217 | * Identifies a bootable file and return a suitable loader | 210 | * Identifies a bootable file and return a suitable loader |
| 218 | * @param file The bootable file | 211 | * @param file The bootable file |
| 219 | * @return the best loader for this file | 212 | * @return the best loader for this file |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 908d91eab..2179cf2ea 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -186,7 +186,6 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 186 | } | 186 | } |
| 187 | 187 | ||
| 188 | process->svc_access_mask.set(); | 188 | process->svc_access_mask.set(); |
| 189 | process->address_mappings = default_address_mappings; | ||
| 190 | process->resource_limit = | 189 | process->resource_limit = |
| 191 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | 190 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); |
| 192 | process->Run(base_addr, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); | 191 | process->Run(base_addr, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index fee7d58c6..a94558ac5 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -152,7 +152,6 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 152 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR); | 152 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR); |
| 153 | 153 | ||
| 154 | process->svc_access_mask.set(); | 154 | process->svc_access_mask.set(); |
| 155 | process->address_mappings = default_address_mappings; | ||
| 156 | process->resource_limit = | 155 | process->resource_limit = |
| 157 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | 156 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); |
| 158 | process->Run(Memory::PROCESS_IMAGE_VADDR, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); | 157 | process->Run(Memory::PROCESS_IMAGE_VADDR, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 9f64b248b..2526ebf28 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -200,6 +200,14 @@ enum class IMinMaxExchange : u64 { | |||
| 200 | XHi = 3, | 200 | XHi = 3, |
| 201 | }; | 201 | }; |
| 202 | 202 | ||
| 203 | enum class XmadMode : u64 { | ||
| 204 | None = 0, | ||
| 205 | CLo = 1, | ||
| 206 | CHi = 2, | ||
| 207 | CSfu = 3, | ||
| 208 | CBcc = 4, | ||
| 209 | }; | ||
| 210 | |||
| 203 | enum class FlowCondition : u64 { | 211 | enum class FlowCondition : u64 { |
| 204 | Always = 0xF, | 212 | Always = 0xF, |
| 205 | Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? | 213 | Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? |
| @@ -457,6 +465,18 @@ union Instruction { | |||
| 457 | } bra; | 465 | } bra; |
| 458 | 466 | ||
| 459 | union { | 467 | union { |
| 468 | BitField<20, 16, u64> imm20_16; | ||
| 469 | BitField<36, 1, u64> product_shift_left; | ||
| 470 | BitField<37, 1, u64> merge_37; | ||
| 471 | BitField<48, 1, u64> sign_a; | ||
| 472 | BitField<49, 1, u64> sign_b; | ||
| 473 | BitField<50, 3, XmadMode> mode; | ||
| 474 | BitField<52, 1, u64> high_b; | ||
| 475 | BitField<53, 1, u64> high_a; | ||
| 476 | BitField<56, 1, u64> merge_56; | ||
| 477 | } xmad; | ||
| 478 | |||
| 479 | union { | ||
| 460 | BitField<20, 14, u64> offset; | 480 | BitField<20, 14, u64> offset; |
| 461 | BitField<34, 5, u64> index; | 481 | BitField<34, 5, u64> index; |
| 462 | } cbuf34; | 482 | } cbuf34; |
| @@ -593,6 +613,7 @@ public: | |||
| 593 | IntegerSetPredicate, | 613 | IntegerSetPredicate, |
| 594 | PredicateSetPredicate, | 614 | PredicateSetPredicate, |
| 595 | Conversion, | 615 | Conversion, |
| 616 | Xmad, | ||
| 596 | Unknown, | 617 | Unknown, |
| 597 | }; | 618 | }; |
| 598 | 619 | ||
| @@ -782,10 +803,10 @@ private: | |||
| 782 | INST("010010110101----", Id::ISET_C, Type::IntegerSet, "ISET_C"), | 803 | INST("010010110101----", Id::ISET_C, Type::IntegerSet, "ISET_C"), |
| 783 | INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"), | 804 | INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"), |
| 784 | INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), | 805 | INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), |
| 785 | INST("0011011-00------", Id::XMAD_IMM, Type::Arithmetic, "XMAD_IMM"), | 806 | INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"), |
| 786 | INST("0100111---------", Id::XMAD_CR, Type::Arithmetic, "XMAD_CR"), | 807 | INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"), |
| 787 | INST("010100010-------", Id::XMAD_RC, Type::Arithmetic, "XMAD_RC"), | 808 | INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"), |
| 788 | INST("0101101100------", Id::XMAD_RR, Type::Arithmetic, "XMAD_RR"), | 809 | INST("0101101100------", Id::XMAD_RR, Type::Xmad, "XMAD_RR"), |
| 789 | }; | 810 | }; |
| 790 | #undef INST | 811 | #undef INST |
| 791 | std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) { | 812 | std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) { |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 19e7f1161..5a593c1f7 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -46,8 +46,11 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { | |||
| 46 | case RenderTargetFormat::RGBA32_FLOAT: | 46 | case RenderTargetFormat::RGBA32_FLOAT: |
| 47 | case RenderTargetFormat::RGBA32_UINT: | 47 | case RenderTargetFormat::RGBA32_UINT: |
| 48 | return 16; | 48 | return 16; |
| 49 | case RenderTargetFormat::RGBA16_UINT: | ||
| 50 | case RenderTargetFormat::RGBA16_UNORM: | ||
| 49 | case RenderTargetFormat::RGBA16_FLOAT: | 51 | case RenderTargetFormat::RGBA16_FLOAT: |
| 50 | case RenderTargetFormat::RG32_FLOAT: | 52 | case RenderTargetFormat::RG32_FLOAT: |
| 53 | case RenderTargetFormat::RG32_UINT: | ||
| 51 | return 8; | 54 | return 8; |
| 52 | case RenderTargetFormat::RGBA8_UNORM: | 55 | case RenderTargetFormat::RGBA8_UNORM: |
| 53 | case RenderTargetFormat::RGBA8_SNORM: | 56 | case RenderTargetFormat::RGBA8_SNORM: |
| @@ -61,12 +64,14 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { | |||
| 61 | case RenderTargetFormat::RG16_FLOAT: | 64 | case RenderTargetFormat::RG16_FLOAT: |
| 62 | case RenderTargetFormat::R32_FLOAT: | 65 | case RenderTargetFormat::R32_FLOAT: |
| 63 | case RenderTargetFormat::R11G11B10_FLOAT: | 66 | case RenderTargetFormat::R11G11B10_FLOAT: |
| 67 | case RenderTargetFormat::R32_UINT: | ||
| 64 | return 4; | 68 | return 4; |
| 65 | case RenderTargetFormat::R16_UNORM: | 69 | case RenderTargetFormat::R16_UNORM: |
| 66 | case RenderTargetFormat::R16_SNORM: | 70 | case RenderTargetFormat::R16_SNORM: |
| 67 | case RenderTargetFormat::R16_UINT: | 71 | case RenderTargetFormat::R16_UINT: |
| 68 | case RenderTargetFormat::R16_SINT: | 72 | case RenderTargetFormat::R16_SINT: |
| 69 | case RenderTargetFormat::R16_FLOAT: | 73 | case RenderTargetFormat::R16_FLOAT: |
| 74 | case RenderTargetFormat::RG8_UNORM: | ||
| 70 | case RenderTargetFormat::RG8_SNORM: | 75 | case RenderTargetFormat::RG8_SNORM: |
| 71 | return 2; | 76 | return 2; |
| 72 | case RenderTargetFormat::R8_UNORM: | 77 | case RenderTargetFormat::R8_UNORM: |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index e008d8f26..97dcccb92 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -20,8 +20,11 @@ enum class RenderTargetFormat : u32 { | |||
| 20 | NONE = 0x0, | 20 | NONE = 0x0, |
| 21 | RGBA32_FLOAT = 0xC0, | 21 | RGBA32_FLOAT = 0xC0, |
| 22 | RGBA32_UINT = 0xC2, | 22 | RGBA32_UINT = 0xC2, |
| 23 | RGBA16_UNORM = 0xC6, | ||
| 24 | RGBA16_UINT = 0xC9, | ||
| 23 | RGBA16_FLOAT = 0xCA, | 25 | RGBA16_FLOAT = 0xCA, |
| 24 | RG32_FLOAT = 0xCB, | 26 | RG32_FLOAT = 0xCB, |
| 27 | RG32_UINT = 0xCD, | ||
| 25 | BGRA8_UNORM = 0xCF, | 28 | BGRA8_UNORM = 0xCF, |
| 26 | RGB10_A2_UNORM = 0xD1, | 29 | RGB10_A2_UNORM = 0xD1, |
| 27 | RGBA8_UNORM = 0xD5, | 30 | RGBA8_UNORM = 0xD5, |
| @@ -33,8 +36,10 @@ enum class RenderTargetFormat : u32 { | |||
| 33 | RG16_UINT = 0xDD, | 36 | RG16_UINT = 0xDD, |
| 34 | RG16_FLOAT = 0xDE, | 37 | RG16_FLOAT = 0xDE, |
| 35 | R11G11B10_FLOAT = 0xE0, | 38 | R11G11B10_FLOAT = 0xE0, |
| 39 | R32_UINT = 0xE4, | ||
| 36 | R32_FLOAT = 0xE5, | 40 | R32_FLOAT = 0xE5, |
| 37 | B5G6R5_UNORM = 0xE8, | 41 | B5G6R5_UNORM = 0xE8, |
| 42 | RG8_UNORM = 0xEA, | ||
| 38 | RG8_SNORM = 0xEB, | 43 | RG8_SNORM = 0xEB, |
| 39 | R16_UNORM = 0xEE, | 44 | R16_UNORM = 0xEE, |
| 40 | R16_SNORM = 0xEF, | 45 | R16_SNORM = 0xEF, |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 38a7b1413..9d1549fe9 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -36,30 +36,21 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); | |||
| 36 | MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); | 36 | MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); |
| 37 | MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); | 37 | MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); |
| 38 | 38 | ||
| 39 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) : emu_window{window} { | 39 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) |
| 40 | : emu_window{window}, stream_buffer(GL_ARRAY_BUFFER, STREAM_BUFFER_SIZE) { | ||
| 40 | // Create sampler objects | 41 | // Create sampler objects |
| 41 | for (size_t i = 0; i < texture_samplers.size(); ++i) { | 42 | for (size_t i = 0; i < texture_samplers.size(); ++i) { |
| 42 | texture_samplers[i].Create(); | 43 | texture_samplers[i].Create(); |
| 43 | state.texture_units[i].sampler = texture_samplers[i].sampler.handle; | 44 | state.texture_units[i].sampler = texture_samplers[i].sampler.handle; |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | // Create SSBOs | ||
| 47 | for (size_t stage = 0; stage < ssbos.size(); ++stage) { | ||
| 48 | for (size_t buffer = 0; buffer < ssbos[stage].size(); ++buffer) { | ||
| 49 | ssbos[stage][buffer].Create(); | ||
| 50 | state.draw.const_buffers[stage][buffer].ssbo = ssbos[stage][buffer].handle; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | GLint ext_num; | 47 | GLint ext_num; |
| 55 | glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num); | 48 | glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num); |
| 56 | for (GLint i = 0; i < ext_num; i++) { | 49 | for (GLint i = 0; i < ext_num; i++) { |
| 57 | const std::string_view extension{ | 50 | const std::string_view extension{ |
| 58 | reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i))}; | 51 | reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i))}; |
| 59 | 52 | ||
| 60 | if (extension == "GL_ARB_buffer_storage") { | 53 | if (extension == "GL_ARB_direct_state_access") { |
| 61 | has_ARB_buffer_storage = true; | ||
| 62 | } else if (extension == "GL_ARB_direct_state_access") { | ||
| 63 | has_ARB_direct_state_access = true; | 54 | has_ARB_direct_state_access = true; |
| 64 | } else if (extension == "GL_ARB_separate_shader_objects") { | 55 | } else if (extension == "GL_ARB_separate_shader_objects") { |
| 65 | has_ARB_separate_shader_objects = true; | 56 | has_ARB_separate_shader_objects = true; |
| @@ -86,47 +77,31 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) : emu_wind | |||
| 86 | 77 | ||
| 87 | hw_vao.Create(); | 78 | hw_vao.Create(); |
| 88 | 79 | ||
| 89 | stream_buffer = OGLStreamBuffer::MakeBuffer(has_ARB_buffer_storage, GL_ARRAY_BUFFER); | 80 | state.draw.vertex_buffer = stream_buffer.GetHandle(); |
| 90 | stream_buffer->Create(STREAM_BUFFER_SIZE, STREAM_BUFFER_SIZE / 2); | ||
| 91 | state.draw.vertex_buffer = stream_buffer->GetHandle(); | ||
| 92 | 81 | ||
| 93 | shader_program_manager = std::make_unique<GLShader::ProgramManager>(); | 82 | shader_program_manager = std::make_unique<GLShader::ProgramManager>(); |
| 94 | state.draw.shader_program = 0; | 83 | state.draw.shader_program = 0; |
| 95 | state.draw.vertex_array = hw_vao.handle; | 84 | state.draw.vertex_array = hw_vao.handle; |
| 96 | state.Apply(); | 85 | state.Apply(); |
| 97 | 86 | ||
| 98 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer->GetHandle()); | 87 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer.GetHandle()); |
| 99 | |||
| 100 | for (unsigned index = 0; index < uniform_buffers.size(); ++index) { | ||
| 101 | auto& buffer = uniform_buffers[index]; | ||
| 102 | buffer.Create(); | ||
| 103 | glBindBuffer(GL_UNIFORM_BUFFER, buffer.handle); | ||
| 104 | glBufferData(GL_UNIFORM_BUFFER, sizeof(GLShader::MaxwellUniformData), nullptr, | ||
| 105 | GL_STREAM_COPY); | ||
| 106 | glBindBufferBase(GL_UNIFORM_BUFFER, index, buffer.handle); | ||
| 107 | } | ||
| 108 | 88 | ||
| 109 | glEnable(GL_BLEND); | 89 | glEnable(GL_BLEND); |
| 110 | 90 | ||
| 91 | glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); | ||
| 92 | |||
| 111 | LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); | 93 | LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); |
| 112 | } | 94 | } |
| 113 | 95 | ||
| 114 | RasterizerOpenGL::~RasterizerOpenGL() { | 96 | RasterizerOpenGL::~RasterizerOpenGL() {} |
| 115 | if (stream_buffer != nullptr) { | ||
| 116 | state.draw.vertex_buffer = stream_buffer->GetHandle(); | ||
| 117 | state.Apply(); | ||
| 118 | stream_buffer->Release(); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | 97 | ||
| 122 | std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, | 98 | std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, |
| 123 | GLintptr buffer_offset) { | 99 | GLintptr buffer_offset) { |
| 124 | MICROPROFILE_SCOPE(OpenGL_VAO); | 100 | MICROPROFILE_SCOPE(OpenGL_VAO); |
| 125 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 101 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 126 | const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; | ||
| 127 | 102 | ||
| 128 | state.draw.vertex_array = hw_vao.handle; | 103 | state.draw.vertex_array = hw_vao.handle; |
| 129 | state.draw.vertex_buffer = stream_buffer->GetHandle(); | 104 | state.draw.vertex_buffer = stream_buffer.GetHandle(); |
| 130 | state.Apply(); | 105 | state.Apply(); |
| 131 | 106 | ||
| 132 | // Upload all guest vertex arrays sequentially to our buffer | 107 | // Upload all guest vertex arrays sequentially to our buffer |
| @@ -141,16 +116,15 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, | |||
| 141 | ASSERT(end > start); | 116 | ASSERT(end > start); |
| 142 | u64 size = end - start + 1; | 117 | u64 size = end - start + 1; |
| 143 | 118 | ||
| 144 | // Copy vertex array data | 119 | GLintptr vertex_buffer_offset; |
| 145 | Memory::ReadBlock(*memory_manager->GpuToCpuAddress(start), array_ptr, size); | 120 | std::tie(array_ptr, buffer_offset, vertex_buffer_offset) = |
| 121 | UploadMemory(array_ptr, buffer_offset, start, size); | ||
| 146 | 122 | ||
| 147 | // Bind the vertex array to the buffer at the current offset. | 123 | // Bind the vertex array to the buffer at the current offset. |
| 148 | glBindVertexBuffer(index, stream_buffer->GetHandle(), buffer_offset, vertex_array.stride); | 124 | glBindVertexBuffer(index, stream_buffer.GetHandle(), vertex_buffer_offset, |
| 125 | vertex_array.stride); | ||
| 149 | 126 | ||
| 150 | ASSERT_MSG(vertex_array.divisor == 0, "Vertex buffer divisor unimplemented"); | 127 | ASSERT_MSG(vertex_array.divisor == 0, "Vertex buffer divisor unimplemented"); |
| 151 | |||
| 152 | array_ptr += size; | ||
| 153 | buffer_offset += size; | ||
| 154 | } | 128 | } |
| 155 | 129 | ||
| 156 | // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. | 130 | // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. |
| @@ -201,22 +175,12 @@ static GLShader::ProgramCode GetShaderProgramCode(Maxwell::ShaderProgram program | |||
| 201 | return program_code; | 175 | return program_code; |
| 202 | } | 176 | } |
| 203 | 177 | ||
| 204 | void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { | 178 | std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { |
| 205 | // Helper function for uploading uniform data | ||
| 206 | const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) { | ||
| 207 | if (has_ARB_direct_state_access) { | ||
| 208 | glCopyNamedBufferSubData(stream_buffer->GetHandle(), handle, offset, 0, size); | ||
| 209 | } else { | ||
| 210 | glBindBuffer(GL_COPY_WRITE_BUFFER, handle); | ||
| 211 | glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, offset, 0, size); | ||
| 212 | } | ||
| 213 | }; | ||
| 214 | |||
| 215 | auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | 179 | auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); |
| 216 | 180 | ||
| 217 | // Next available bindpoints to use when uploading the const buffers and textures to the GLSL | 181 | // Next available bindpoints to use when uploading the const buffers and textures to the GLSL |
| 218 | // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. | 182 | // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. |
| 219 | u32 current_constbuffer_bindpoint = static_cast<u32>(uniform_buffers.size()); | 183 | u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; |
| 220 | u32 current_texture_bindpoint = 0; | 184 | u32 current_texture_bindpoint = 0; |
| 221 | 185 | ||
| 222 | for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { | 186 | for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { |
| @@ -228,22 +192,21 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { | |||
| 228 | continue; | 192 | continue; |
| 229 | } | 193 | } |
| 230 | 194 | ||
| 195 | std::tie(buffer_ptr, buffer_offset) = | ||
| 196 | AlignBuffer(buffer_ptr, buffer_offset, static_cast<size_t>(uniform_buffer_alignment)); | ||
| 197 | |||
| 231 | const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 | 198 | const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 |
| 232 | 199 | ||
| 233 | GLShader::MaxwellUniformData ubo{}; | 200 | GLShader::MaxwellUniformData ubo{}; |
| 234 | ubo.SetFromRegs(gpu.state.shader_stages[stage]); | 201 | ubo.SetFromRegs(gpu.state.shader_stages[stage]); |
| 235 | std::memcpy(buffer_ptr, &ubo, sizeof(ubo)); | 202 | std::memcpy(buffer_ptr, &ubo, sizeof(ubo)); |
| 236 | 203 | ||
| 237 | // Flush the buffer so that the GPU can see the data we just wrote. | 204 | // Bind the buffer |
| 238 | glFlushMappedBufferRange(GL_ARRAY_BUFFER, buffer_offset, sizeof(ubo)); | 205 | glBindBufferRange(GL_UNIFORM_BUFFER, stage, stream_buffer.GetHandle(), buffer_offset, |
| 239 | 206 | sizeof(ubo)); | |
| 240 | // Upload uniform data as one UBO per stage | ||
| 241 | const GLintptr ubo_offset = buffer_offset; | ||
| 242 | copy_buffer(uniform_buffers[stage].handle, ubo_offset, | ||
| 243 | sizeof(GLShader::MaxwellUniformData)); | ||
| 244 | 207 | ||
| 245 | buffer_ptr += sizeof(GLShader::MaxwellUniformData); | 208 | buffer_ptr += sizeof(ubo); |
| 246 | buffer_offset += sizeof(GLShader::MaxwellUniformData); | 209 | buffer_offset += sizeof(ubo); |
| 247 | 210 | ||
| 248 | GLShader::ShaderSetup setup{GetShaderProgramCode(program)}; | 211 | GLShader::ShaderSetup setup{GetShaderProgramCode(program)}; |
| 249 | GLShader::ShaderEntries shader_resources; | 212 | GLShader::ShaderEntries shader_resources; |
| @@ -282,9 +245,9 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { | |||
| 282 | static_cast<Maxwell::ShaderStage>(stage)); | 245 | static_cast<Maxwell::ShaderStage>(stage)); |
| 283 | 246 | ||
| 284 | // Configure the const buffers for this shader stage. | 247 | // Configure the const buffers for this shader stage. |
| 285 | current_constbuffer_bindpoint = | 248 | std::tie(buffer_ptr, buffer_offset, current_constbuffer_bindpoint) = SetupConstBuffers( |
| 286 | SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, | 249 | buffer_ptr, buffer_offset, static_cast<Maxwell::ShaderStage>(stage), gl_stage_program, |
| 287 | current_constbuffer_bindpoint, shader_resources.const_buffer_entries); | 250 | current_constbuffer_bindpoint, shader_resources.const_buffer_entries); |
| 288 | 251 | ||
| 289 | // Configure the textures for this shader stage. | 252 | // Configure the textures for this shader stage. |
| 290 | current_texture_bindpoint = | 253 | current_texture_bindpoint = |
| @@ -299,6 +262,8 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { | |||
| 299 | } | 262 | } |
| 300 | 263 | ||
| 301 | shader_program_manager->UseTrivialGeometryShader(); | 264 | shader_program_manager->UseTrivialGeometryShader(); |
| 265 | |||
| 266 | return {buffer_ptr, buffer_offset}; | ||
| 302 | } | 267 | } |
| 303 | 268 | ||
| 304 | size_t RasterizerOpenGL::CalculateVertexArraysSize() const { | 269 | size_t RasterizerOpenGL::CalculateVertexArraysSize() const { |
| @@ -432,6 +397,31 @@ void RasterizerOpenGL::Clear() { | |||
| 432 | } | 397 | } |
| 433 | } | 398 | } |
| 434 | 399 | ||
| 400 | std::pair<u8*, GLintptr> RasterizerOpenGL::AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, | ||
| 401 | size_t alignment) { | ||
| 402 | // Align the offset, not the mapped pointer | ||
| 403 | GLintptr offset_aligned = | ||
| 404 | static_cast<GLintptr>(Common::AlignUp(static_cast<size_t>(buffer_offset), alignment)); | ||
| 405 | return {buffer_ptr + (offset_aligned - buffer_offset), offset_aligned}; | ||
| 406 | } | ||
| 407 | |||
| 408 | std::tuple<u8*, GLintptr, GLintptr> RasterizerOpenGL::UploadMemory(u8* buffer_ptr, | ||
| 409 | GLintptr buffer_offset, | ||
| 410 | Tegra::GPUVAddr gpu_addr, | ||
| 411 | size_t size, size_t alignment) { | ||
| 412 | std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, alignment); | ||
| 413 | GLintptr uploaded_offset = buffer_offset; | ||
| 414 | |||
| 415 | const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; | ||
| 416 | const boost::optional<VAddr> cpu_addr{memory_manager->GpuToCpuAddress(gpu_addr)}; | ||
| 417 | Memory::ReadBlock(*cpu_addr, buffer_ptr, size); | ||
| 418 | |||
| 419 | buffer_ptr += size; | ||
| 420 | buffer_offset += size; | ||
| 421 | |||
| 422 | return {buffer_ptr, buffer_offset, uploaded_offset}; | ||
| 423 | } | ||
| 424 | |||
| 435 | void RasterizerOpenGL::DrawArrays() { | 425 | void RasterizerOpenGL::DrawArrays() { |
| 436 | if (accelerate_draw == AccelDraw::Disabled) | 426 | if (accelerate_draw == AccelDraw::Disabled) |
| 437 | return; | 427 | return; |
| @@ -456,7 +446,7 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 456 | const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()}; | 446 | const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()}; |
| 457 | const unsigned vertex_num{is_indexed ? regs.index_array.count : regs.vertex_buffer.count}; | 447 | const unsigned vertex_num{is_indexed ? regs.index_array.count : regs.vertex_buffer.count}; |
| 458 | 448 | ||
| 459 | state.draw.vertex_buffer = stream_buffer->GetHandle(); | 449 | state.draw.vertex_buffer = stream_buffer.GetHandle(); |
| 460 | state.Apply(); | 450 | state.Apply(); |
| 461 | 451 | ||
| 462 | size_t buffer_size = CalculateVertexArraysSize(); | 452 | size_t buffer_size = CalculateVertexArraysSize(); |
| @@ -466,41 +456,31 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 466 | } | 456 | } |
| 467 | 457 | ||
| 468 | // Uniform space for the 5 shader stages | 458 | // Uniform space for the 5 shader stages |
| 469 | buffer_size = Common::AlignUp<size_t>(buffer_size, 4) + | 459 | buffer_size = |
| 470 | sizeof(GLShader::MaxwellUniformData) * Maxwell::MaxShaderStage; | 460 | Common::AlignUp<size_t>(buffer_size, 4) + |
| 461 | (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; | ||
| 462 | |||
| 463 | // Add space for at least 18 constant buffers | ||
| 464 | buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); | ||
| 471 | 465 | ||
| 472 | u8* buffer_ptr; | 466 | u8* buffer_ptr; |
| 473 | GLintptr buffer_offset; | 467 | GLintptr buffer_offset; |
| 474 | std::tie(buffer_ptr, buffer_offset) = | 468 | std::tie(buffer_ptr, buffer_offset, std::ignore) = |
| 475 | stream_buffer->Map(static_cast<GLsizeiptr>(buffer_size), 4); | 469 | stream_buffer.Map(static_cast<GLsizeiptr>(buffer_size), 4); |
| 470 | u8* buffer_ptr_base = buffer_ptr; | ||
| 476 | 471 | ||
| 477 | u8* offseted_buffer; | 472 | std::tie(buffer_ptr, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset); |
| 478 | std::tie(offseted_buffer, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset); | ||
| 479 | |||
| 480 | offseted_buffer = | ||
| 481 | reinterpret_cast<u8*>(Common::AlignUp(reinterpret_cast<size_t>(offseted_buffer), 4)); | ||
| 482 | buffer_offset = Common::AlignUp<size_t>(buffer_offset, 4); | ||
| 483 | 473 | ||
| 484 | // If indexed mode, copy the index buffer | 474 | // If indexed mode, copy the index buffer |
| 485 | GLintptr index_buffer_offset = 0; | 475 | GLintptr index_buffer_offset = 0; |
| 486 | if (is_indexed) { | 476 | if (is_indexed) { |
| 487 | const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; | 477 | std::tie(buffer_ptr, buffer_offset, index_buffer_offset) = UploadMemory( |
| 488 | const boost::optional<VAddr> index_data_addr{ | 478 | buffer_ptr, buffer_offset, regs.index_array.StartAddress(), index_buffer_size); |
| 489 | memory_manager->GpuToCpuAddress(regs.index_array.StartAddress())}; | ||
| 490 | Memory::ReadBlock(*index_data_addr, offseted_buffer, index_buffer_size); | ||
| 491 | |||
| 492 | index_buffer_offset = buffer_offset; | ||
| 493 | offseted_buffer += index_buffer_size; | ||
| 494 | buffer_offset += index_buffer_size; | ||
| 495 | } | 479 | } |
| 496 | 480 | ||
| 497 | offseted_buffer = | 481 | std::tie(buffer_ptr, buffer_offset) = SetupShaders(buffer_ptr, buffer_offset); |
| 498 | reinterpret_cast<u8*>(Common::AlignUp(reinterpret_cast<size_t>(offseted_buffer), 4)); | ||
| 499 | buffer_offset = Common::AlignUp<size_t>(buffer_offset, 4); | ||
| 500 | |||
| 501 | SetupShaders(offseted_buffer, buffer_offset); | ||
| 502 | 482 | ||
| 503 | stream_buffer->Unmap(); | 483 | stream_buffer.Unmap(buffer_ptr - buffer_ptr_base); |
| 504 | 484 | ||
| 505 | shader_program_manager->ApplyTo(state); | 485 | shader_program_manager->ApplyTo(state); |
| 506 | state.Apply(); | 486 | state.Apply(); |
| @@ -647,45 +627,32 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr | |||
| 647 | } | 627 | } |
| 648 | } | 628 | } |
| 649 | 629 | ||
| 650 | u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program, | 630 | std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers( |
| 651 | u32 current_bindpoint, | 631 | u8* buffer_ptr, GLintptr buffer_offset, Maxwell::ShaderStage stage, GLuint program, |
| 652 | const std::vector<GLShader::ConstBufferEntry>& entries) { | 632 | u32 current_bindpoint, const std::vector<GLShader::ConstBufferEntry>& entries) { |
| 653 | const auto& gpu = Core::System::GetInstance().GPU(); | 633 | const auto& gpu = Core::System::GetInstance().GPU(); |
| 654 | const auto& maxwell3d = gpu.Maxwell3D(); | 634 | const auto& maxwell3d = gpu.Maxwell3D(); |
| 655 | 635 | ||
| 656 | // Reset all buffer draw state for this stage. | ||
| 657 | for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) { | ||
| 658 | buffer.bindpoint = 0; | ||
| 659 | buffer.enabled = false; | ||
| 660 | } | ||
| 661 | |||
| 662 | // Upload only the enabled buffers from the 16 constbuffers of each shader stage | 636 | // Upload only the enabled buffers from the 16 constbuffers of each shader stage |
| 663 | const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)]; | 637 | const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)]; |
| 664 | 638 | ||
| 665 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { | 639 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { |
| 666 | const auto& used_buffer = entries[bindpoint]; | 640 | const auto& used_buffer = entries[bindpoint]; |
| 667 | const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; | 641 | const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; |
| 668 | auto& buffer_draw_state = | ||
| 669 | state.draw.const_buffers[static_cast<size_t>(stage)][used_buffer.GetIndex()]; | ||
| 670 | 642 | ||
| 671 | if (!buffer.enabled) { | 643 | if (!buffer.enabled) { |
| 672 | continue; | 644 | continue; |
| 673 | } | 645 | } |
| 674 | 646 | ||
| 675 | buffer_draw_state.enabled = true; | ||
| 676 | buffer_draw_state.bindpoint = current_bindpoint + bindpoint; | ||
| 677 | |||
| 678 | boost::optional<VAddr> addr = gpu.memory_manager->GpuToCpuAddress(buffer.address); | ||
| 679 | |||
| 680 | size_t size = 0; | 647 | size_t size = 0; |
| 681 | 648 | ||
| 682 | if (used_buffer.IsIndirect()) { | 649 | if (used_buffer.IsIndirect()) { |
| 683 | // Buffer is accessed indirectly, so upload the entire thing | 650 | // Buffer is accessed indirectly, so upload the entire thing |
| 684 | size = buffer.size * sizeof(float); | 651 | size = buffer.size; |
| 685 | 652 | ||
| 686 | if (size > MaxConstbufferSize) { | 653 | if (size > MaxConstbufferSize) { |
| 687 | LOG_ERROR(HW_GPU, "indirect constbuffer size {} exceeds maximum {}", size, | 654 | LOG_CRITICAL(HW_GPU, "indirect constbuffer size {} exceeds maximum {}", size, |
| 688 | MaxConstbufferSize); | 655 | MaxConstbufferSize); |
| 689 | size = MaxConstbufferSize; | 656 | size = MaxConstbufferSize; |
| 690 | } | 657 | } |
| 691 | } else { | 658 | } else { |
| @@ -698,25 +665,26 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr | |||
| 698 | size = Common::AlignUp(size, sizeof(GLvec4)); | 665 | size = Common::AlignUp(size, sizeof(GLvec4)); |
| 699 | ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); | 666 | ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); |
| 700 | 667 | ||
| 701 | std::vector<u8> data(size); | 668 | GLintptr const_buffer_offset; |
| 702 | Memory::ReadBlock(*addr, data.data(), data.size()); | 669 | std::tie(buffer_ptr, buffer_offset, const_buffer_offset) = |
| 670 | UploadMemory(buffer_ptr, buffer_offset, buffer.address, size, | ||
| 671 | static_cast<size_t>(uniform_buffer_alignment)); | ||
| 703 | 672 | ||
| 704 | glBindBuffer(GL_UNIFORM_BUFFER, buffer_draw_state.ssbo); | 673 | glBindBufferRange(GL_UNIFORM_BUFFER, current_bindpoint + bindpoint, |
| 705 | glBufferData(GL_UNIFORM_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW); | 674 | stream_buffer.GetHandle(), const_buffer_offset, size); |
| 706 | glBindBuffer(GL_UNIFORM_BUFFER, 0); | ||
| 707 | 675 | ||
| 708 | // Now configure the bindpoint of the buffer inside the shader | 676 | // Now configure the bindpoint of the buffer inside the shader |
| 709 | const std::string buffer_name = used_buffer.GetName(); | 677 | const std::string buffer_name = used_buffer.GetName(); |
| 710 | const GLuint index = | 678 | const GLuint index = |
| 711 | glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, buffer_name.c_str()); | 679 | glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, buffer_name.c_str()); |
| 712 | if (index != GL_INVALID_INDEX) { | 680 | if (index != GL_INVALID_INDEX) { |
| 713 | glUniformBlockBinding(program, index, buffer_draw_state.bindpoint); | 681 | glUniformBlockBinding(program, index, current_bindpoint + bindpoint); |
| 714 | } | 682 | } |
| 715 | } | 683 | } |
| 716 | 684 | ||
| 717 | state.Apply(); | 685 | state.Apply(); |
| 718 | 686 | ||
| 719 | return current_bindpoint + static_cast<u32>(entries.size()); | 687 | return {buffer_ptr, buffer_offset, current_bindpoint + static_cast<u32>(entries.size())}; |
| 720 | } | 688 | } |
| 721 | 689 | ||
| 722 | u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit, | 690 | u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit, |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index bd01dc0ae..74307f626 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <memory> | 9 | #include <memory> |
| 10 | #include <tuple> | ||
| 10 | #include <utility> | 11 | #include <utility> |
| 11 | #include <vector> | 12 | #include <vector> |
| 12 | #include <glad/glad.h> | 13 | #include <glad/glad.h> |
| @@ -100,9 +101,10 @@ private: | |||
| 100 | * @param entries Vector describing the buffers that are actually used in the guest shader. | 101 | * @param entries Vector describing the buffers that are actually used in the guest shader. |
| 101 | * @returns The next available bindpoint for use in the next shader stage. | 102 | * @returns The next available bindpoint for use in the next shader stage. |
| 102 | */ | 103 | */ |
| 103 | u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, GLuint program, | 104 | std::tuple<u8*, GLintptr, u32> SetupConstBuffers( |
| 104 | u32 current_bindpoint, | 105 | u8* buffer_ptr, GLintptr buffer_offset, Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, |
| 105 | const std::vector<GLShader::ConstBufferEntry>& entries); | 106 | GLuint program, u32 current_bindpoint, |
| 107 | const std::vector<GLShader::ConstBufferEntry>& entries); | ||
| 106 | 108 | ||
| 107 | /* | 109 | /* |
| 108 | * Configures the current textures to use for the draw command. | 110 | * Configures the current textures to use for the draw command. |
| @@ -139,7 +141,6 @@ private: | |||
| 139 | /// Syncs the blend state to match the guest state | 141 | /// Syncs the blend state to match the guest state |
| 140 | void SyncBlendState(); | 142 | void SyncBlendState(); |
| 141 | 143 | ||
| 142 | bool has_ARB_buffer_storage = false; | ||
| 143 | bool has_ARB_direct_state_access = false; | 144 | bool has_ARB_direct_state_access = false; |
| 144 | bool has_ARB_separate_shader_objects = false; | 145 | bool has_ARB_separate_shader_objects = false; |
| 145 | bool has_ARB_vertex_attrib_binding = false; | 146 | bool has_ARB_vertex_attrib_binding = false; |
| @@ -155,22 +156,24 @@ private: | |||
| 155 | OGLVertexArray hw_vao; | 156 | OGLVertexArray hw_vao; |
| 156 | 157 | ||
| 157 | std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers; | 158 | std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers; |
| 158 | std::array<std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers>, | ||
| 159 | Tegra::Engines::Maxwell3D::Regs::MaxShaderStage> | ||
| 160 | ssbos; | ||
| 161 | 159 | ||
| 162 | static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; | 160 | static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; |
| 163 | std::unique_ptr<OGLStreamBuffer> stream_buffer; | 161 | OGLStreamBuffer stream_buffer; |
| 164 | OGLBuffer uniform_buffer; | 162 | OGLBuffer uniform_buffer; |
| 165 | OGLFramebuffer framebuffer; | 163 | OGLFramebuffer framebuffer; |
| 164 | GLint uniform_buffer_alignment; | ||
| 166 | 165 | ||
| 167 | size_t CalculateVertexArraysSize() const; | 166 | size_t CalculateVertexArraysSize() const; |
| 168 | 167 | ||
| 169 | std::pair<u8*, GLintptr> SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset); | 168 | std::pair<u8*, GLintptr> SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset); |
| 170 | 169 | ||
| 171 | std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::MaxShaderStage> uniform_buffers; | 170 | std::pair<u8*, GLintptr> SetupShaders(u8* buffer_ptr, GLintptr buffer_offset); |
| 172 | 171 | ||
| 173 | void SetupShaders(u8* buffer_ptr, GLintptr buffer_offset); | 172 | std::pair<u8*, GLintptr> AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, size_t alignment); |
| 173 | |||
| 174 | std::tuple<u8*, GLintptr, GLintptr> UploadMemory(u8* buffer_ptr, GLintptr buffer_offset, | ||
| 175 | Tegra::GPUVAddr gpu_addr, size_t size, | ||
| 176 | size_t alignment = 4); | ||
| 174 | 177 | ||
| 175 | enum class AccelDraw { Disabled, Arrays, Indexed }; | 178 | enum class AccelDraw { Disabled, Arrays, Indexed }; |
| 176 | AccelDraw accelerate_draw = AccelDraw::Disabled; | 179 | AccelDraw accelerate_draw = AccelDraw::Disabled; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 84c250c63..b6947b97b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -94,13 +94,15 @@ struct FormatTuple { | |||
| 94 | static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ | 94 | static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ |
| 95 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U | 95 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U |
| 96 | {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S | 96 | {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S |
| 97 | {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5 | 97 | {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5U |
| 98 | {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm, | 98 | {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm, |
| 99 | false}, // A2B10G10R10 | 99 | false}, // A2B10G10R10U |
| 100 | {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5 | 100 | {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5U |
| 101 | {GL_R8, GL_RED, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // R8 | 101 | {GL_R8, GL_RED, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // R8U |
| 102 | {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI | 102 | {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI |
| 103 | {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F | 103 | {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F |
| 104 | {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RGBA16U | ||
| 105 | {GL_RGBA16UI, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI | ||
| 104 | {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float, | 106 | {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float, |
| 105 | false}, // R11FG11FB10F | 107 | false}, // R11FG11FB10F |
| 106 | {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI | 108 | {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI |
| @@ -114,16 +116,17 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form | |||
| 114 | {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, | 116 | {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, |
| 115 | true}, // DXN2UNORM | 117 | true}, // DXN2UNORM |
| 116 | {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM | 118 | {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM |
| 117 | {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, | 119 | {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, |
| 118 | true}, // BC7U | 120 | true}, // BC7U |
| 119 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 | 121 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 |
| 120 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8 | 122 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U |
| 123 | {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // G8R8S | ||
| 121 | {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8 | 124 | {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8 |
| 122 | {GL_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F | 125 | {GL_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F |
| 123 | {GL_RG32F, GL_RG, GL_FLOAT, ComponentType::Float, false}, // RG32F | 126 | {GL_RG32F, GL_RG, GL_FLOAT, ComponentType::Float, false}, // RG32F |
| 124 | {GL_R32F, GL_RED, GL_FLOAT, ComponentType::Float, false}, // R32F | 127 | {GL_R32F, GL_RED, GL_FLOAT, ComponentType::Float, false}, // R32F |
| 125 | {GL_R16F, GL_RED, GL_HALF_FLOAT, ComponentType::Float, false}, // R16F | 128 | {GL_R16F, GL_RED, GL_HALF_FLOAT, ComponentType::Float, false}, // R16F |
| 126 | {GL_R16, GL_RED, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // R16UNORM | 129 | {GL_R16, GL_RED, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // R16U |
| 127 | {GL_R16_SNORM, GL_RED, GL_SHORT, ComponentType::SNorm, false}, // R16S | 130 | {GL_R16_SNORM, GL_RED, GL_SHORT, ComponentType::SNorm, false}, // R16S |
| 128 | {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // R16UI | 131 | {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // R16UI |
| 129 | {GL_R16I, GL_RED_INTEGER, GL_SHORT, ComponentType::SInt, false}, // R16I | 132 | {GL_R16I, GL_RED_INTEGER, GL_SHORT, ComponentType::SInt, false}, // R16I |
| @@ -134,7 +137,10 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form | |||
| 134 | {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S | 137 | {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S |
| 135 | {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F | 138 | {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F |
| 136 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8 | 139 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8 |
| 140 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U | ||
| 137 | {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S | 141 | {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S |
| 142 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI | ||
| 143 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI | ||
| 138 | 144 | ||
| 139 | // DepthStencil formats | 145 | // DepthStencil formats |
| 140 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm, | 146 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm, |
| @@ -234,40 +240,71 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_bu | |||
| 234 | static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr), | 240 | static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr), |
| 235 | SurfaceParams::MaxPixelFormat> | 241 | SurfaceParams::MaxPixelFormat> |
| 236 | morton_to_gl_fns = { | 242 | morton_to_gl_fns = { |
| 237 | MortonCopy<true, PixelFormat::ABGR8U>, MortonCopy<true, PixelFormat::ABGR8S>, | 243 | // clang-format off |
| 238 | MortonCopy<true, PixelFormat::B5G6R5>, MortonCopy<true, PixelFormat::A2B10G10R10>, | 244 | MortonCopy<true, PixelFormat::ABGR8U>, |
| 239 | MortonCopy<true, PixelFormat::A1B5G5R5>, MortonCopy<true, PixelFormat::R8>, | 245 | MortonCopy<true, PixelFormat::ABGR8S>, |
| 240 | MortonCopy<true, PixelFormat::R8UI>, MortonCopy<true, PixelFormat::RGBA16F>, | 246 | MortonCopy<true, PixelFormat::B5G6R5U>, |
| 241 | MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>, | 247 | MortonCopy<true, PixelFormat::A2B10G10R10U>, |
| 242 | MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>, | 248 | MortonCopy<true, PixelFormat::A1B5G5R5U>, |
| 243 | MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>, | 249 | MortonCopy<true, PixelFormat::R8U>, |
| 244 | MortonCopy<true, PixelFormat::DXN2UNORM>, MortonCopy<true, PixelFormat::DXN2SNORM>, | 250 | MortonCopy<true, PixelFormat::R8UI>, |
| 245 | MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>, | 251 | MortonCopy<true, PixelFormat::RGBA16F>, |
| 246 | MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::BGRA8>, | 252 | MortonCopy<true, PixelFormat::RGBA16U>, |
| 247 | MortonCopy<true, PixelFormat::RGBA32F>, MortonCopy<true, PixelFormat::RG32F>, | 253 | MortonCopy<true, PixelFormat::RGBA16UI>, |
| 248 | MortonCopy<true, PixelFormat::R32F>, MortonCopy<true, PixelFormat::R16F>, | 254 | MortonCopy<true, PixelFormat::R11FG11FB10F>, |
| 249 | MortonCopy<true, PixelFormat::R16UNORM>, MortonCopy<true, PixelFormat::R16S>, | 255 | MortonCopy<true, PixelFormat::RGBA32UI>, |
| 250 | MortonCopy<true, PixelFormat::R16UI>, MortonCopy<true, PixelFormat::R16I>, | 256 | MortonCopy<true, PixelFormat::DXT1>, |
| 251 | MortonCopy<true, PixelFormat::RG16>, MortonCopy<true, PixelFormat::RG16F>, | 257 | MortonCopy<true, PixelFormat::DXT23>, |
| 252 | MortonCopy<true, PixelFormat::RG16UI>, MortonCopy<true, PixelFormat::RG16I>, | 258 | MortonCopy<true, PixelFormat::DXT45>, |
| 253 | MortonCopy<true, PixelFormat::RG16S>, MortonCopy<true, PixelFormat::RGB32F>, | 259 | MortonCopy<true, PixelFormat::DXN1>, |
| 254 | MortonCopy<true, PixelFormat::SRGBA8>, MortonCopy<true, PixelFormat::RG8S>, | 260 | MortonCopy<true, PixelFormat::DXN2UNORM>, |
| 255 | MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>, | 261 | MortonCopy<true, PixelFormat::DXN2SNORM>, |
| 256 | MortonCopy<true, PixelFormat::Z32F>, MortonCopy<true, PixelFormat::Z16>, | 262 | MortonCopy<true, PixelFormat::BC7U>, |
| 263 | MortonCopy<true, PixelFormat::ASTC_2D_4X4>, | ||
| 264 | MortonCopy<true, PixelFormat::G8R8U>, | ||
| 265 | MortonCopy<true, PixelFormat::G8R8S>, | ||
| 266 | MortonCopy<true, PixelFormat::BGRA8>, | ||
| 267 | MortonCopy<true, PixelFormat::RGBA32F>, | ||
| 268 | MortonCopy<true, PixelFormat::RG32F>, | ||
| 269 | MortonCopy<true, PixelFormat::R32F>, | ||
| 270 | MortonCopy<true, PixelFormat::R16F>, | ||
| 271 | MortonCopy<true, PixelFormat::R16U>, | ||
| 272 | MortonCopy<true, PixelFormat::R16S>, | ||
| 273 | MortonCopy<true, PixelFormat::R16UI>, | ||
| 274 | MortonCopy<true, PixelFormat::R16I>, | ||
| 275 | MortonCopy<true, PixelFormat::RG16>, | ||
| 276 | MortonCopy<true, PixelFormat::RG16F>, | ||
| 277 | MortonCopy<true, PixelFormat::RG16UI>, | ||
| 278 | MortonCopy<true, PixelFormat::RG16I>, | ||
| 279 | MortonCopy<true, PixelFormat::RG16S>, | ||
| 280 | MortonCopy<true, PixelFormat::RGB32F>, | ||
| 281 | MortonCopy<true, PixelFormat::SRGBA8>, | ||
| 282 | MortonCopy<true, PixelFormat::RG8U>, | ||
| 283 | MortonCopy<true, PixelFormat::RG8S>, | ||
| 284 | MortonCopy<true, PixelFormat::RG32UI>, | ||
| 285 | MortonCopy<true, PixelFormat::R32UI>, | ||
| 286 | MortonCopy<true, PixelFormat::Z24S8>, | ||
| 287 | MortonCopy<true, PixelFormat::S8Z24>, | ||
| 288 | MortonCopy<true, PixelFormat::Z32F>, | ||
| 289 | MortonCopy<true, PixelFormat::Z16>, | ||
| 257 | MortonCopy<true, PixelFormat::Z32FS8>, | 290 | MortonCopy<true, PixelFormat::Z32FS8>, |
| 291 | // clang-format on | ||
| 258 | }; | 292 | }; |
| 259 | 293 | ||
| 260 | static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr), | 294 | static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr), |
| 261 | SurfaceParams::MaxPixelFormat> | 295 | SurfaceParams::MaxPixelFormat> |
| 262 | gl_to_morton_fns = { | 296 | gl_to_morton_fns = { |
| 297 | // clang-format off | ||
| 263 | MortonCopy<false, PixelFormat::ABGR8U>, | 298 | MortonCopy<false, PixelFormat::ABGR8U>, |
| 264 | MortonCopy<false, PixelFormat::ABGR8S>, | 299 | MortonCopy<false, PixelFormat::ABGR8S>, |
| 265 | MortonCopy<false, PixelFormat::B5G6R5>, | 300 | MortonCopy<false, PixelFormat::B5G6R5U>, |
| 266 | MortonCopy<false, PixelFormat::A2B10G10R10>, | 301 | MortonCopy<false, PixelFormat::A2B10G10R10U>, |
| 267 | MortonCopy<false, PixelFormat::A1B5G5R5>, | 302 | MortonCopy<false, PixelFormat::A1B5G5R5U>, |
| 268 | MortonCopy<false, PixelFormat::R8>, | 303 | MortonCopy<false, PixelFormat::R8U>, |
| 269 | MortonCopy<false, PixelFormat::R8UI>, | 304 | MortonCopy<false, PixelFormat::R8UI>, |
| 270 | MortonCopy<false, PixelFormat::RGBA16F>, | 305 | MortonCopy<false, PixelFormat::RGBA16F>, |
| 306 | MortonCopy<false, PixelFormat::RGBA16U>, | ||
| 307 | MortonCopy<false, PixelFormat::RGBA16UI>, | ||
| 271 | MortonCopy<false, PixelFormat::R11FG11FB10F>, | 308 | MortonCopy<false, PixelFormat::R11FG11FB10F>, |
| 272 | MortonCopy<false, PixelFormat::RGBA32UI>, | 309 | MortonCopy<false, PixelFormat::RGBA32UI>, |
| 273 | // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/ASTC_2D_4X4 formats is not | 310 | // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/ASTC_2D_4X4 formats is not |
| @@ -280,13 +317,14 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU | |||
| 280 | nullptr, | 317 | nullptr, |
| 281 | nullptr, | 318 | nullptr, |
| 282 | nullptr, | 319 | nullptr, |
| 283 | MortonCopy<false, PixelFormat::G8R8>, | 320 | MortonCopy<false, PixelFormat::G8R8U>, |
| 321 | MortonCopy<false, PixelFormat::G8R8S>, | ||
| 284 | MortonCopy<false, PixelFormat::BGRA8>, | 322 | MortonCopy<false, PixelFormat::BGRA8>, |
| 285 | MortonCopy<false, PixelFormat::RGBA32F>, | 323 | MortonCopy<false, PixelFormat::RGBA32F>, |
| 286 | MortonCopy<false, PixelFormat::RG32F>, | 324 | MortonCopy<false, PixelFormat::RG32F>, |
| 287 | MortonCopy<false, PixelFormat::R32F>, | 325 | MortonCopy<false, PixelFormat::R32F>, |
| 288 | MortonCopy<false, PixelFormat::R16F>, | 326 | MortonCopy<false, PixelFormat::R16F>, |
| 289 | MortonCopy<false, PixelFormat::R16UNORM>, | 327 | MortonCopy<false, PixelFormat::R16U>, |
| 290 | MortonCopy<false, PixelFormat::R16S>, | 328 | MortonCopy<false, PixelFormat::R16S>, |
| 291 | MortonCopy<false, PixelFormat::R16UI>, | 329 | MortonCopy<false, PixelFormat::R16UI>, |
| 292 | MortonCopy<false, PixelFormat::R16I>, | 330 | MortonCopy<false, PixelFormat::R16I>, |
| @@ -297,12 +335,16 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU | |||
| 297 | MortonCopy<false, PixelFormat::RG16S>, | 335 | MortonCopy<false, PixelFormat::RG16S>, |
| 298 | MortonCopy<false, PixelFormat::RGB32F>, | 336 | MortonCopy<false, PixelFormat::RGB32F>, |
| 299 | MortonCopy<false, PixelFormat::SRGBA8>, | 337 | MortonCopy<false, PixelFormat::SRGBA8>, |
| 338 | MortonCopy<false, PixelFormat::RG8U>, | ||
| 300 | MortonCopy<false, PixelFormat::RG8S>, | 339 | MortonCopy<false, PixelFormat::RG8S>, |
| 340 | MortonCopy<false, PixelFormat::RG32UI>, | ||
| 341 | MortonCopy<false, PixelFormat::R32UI>, | ||
| 301 | MortonCopy<false, PixelFormat::Z24S8>, | 342 | MortonCopy<false, PixelFormat::Z24S8>, |
| 302 | MortonCopy<false, PixelFormat::S8Z24>, | 343 | MortonCopy<false, PixelFormat::S8Z24>, |
| 303 | MortonCopy<false, PixelFormat::Z32F>, | 344 | MortonCopy<false, PixelFormat::Z32F>, |
| 304 | MortonCopy<false, PixelFormat::Z16>, | 345 | MortonCopy<false, PixelFormat::Z16>, |
| 305 | MortonCopy<false, PixelFormat::Z32FS8>, | 346 | MortonCopy<false, PixelFormat::Z32FS8>, |
| 347 | // clang-format on | ||
| 306 | }; | 348 | }; |
| 307 | 349 | ||
| 308 | // Allocate an uninitialized texture of appropriate size and format for the surface | 350 | // Allocate an uninitialized texture of appropriate size and format for the surface |
| @@ -422,7 +464,7 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { | |||
| 422 | } | 464 | } |
| 423 | 465 | ||
| 424 | static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { | 466 | static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { |
| 425 | const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8)}; | 467 | const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; |
| 426 | for (size_t y = 0; y < height; ++y) { | 468 | for (size_t y = 0; y < height; ++y) { |
| 427 | for (size_t x = 0; x < width; ++x) { | 469 | for (size_t x = 0; x < width; ++x) { |
| 428 | const size_t offset{bpp * (y * width + x)}; | 470 | const size_t offset{bpp * (y * width + x)}; |
| @@ -454,7 +496,8 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma | |||
| 454 | ConvertS8Z24ToZ24S8(data, width, height); | 496 | ConvertS8Z24ToZ24S8(data, width, height); |
| 455 | break; | 497 | break; |
| 456 | 498 | ||
| 457 | case PixelFormat::G8R8: | 499 | case PixelFormat::G8R8U: |
| 500 | case PixelFormat::G8R8S: | ||
| 458 | // Convert the G8R8 color format to R8G8, as OpenGL does not support G8R8. | 501 | // Convert the G8R8 color format to R8G8, as OpenGL does not support G8R8. |
| 459 | ConvertG8R8ToR8G8(data, width, height); | 502 | ConvertG8R8ToR8G8(data, width, height); |
| 460 | break; | 503 | break; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 202257b58..55cf3782c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -25,49 +25,55 @@ struct SurfaceParams { | |||
| 25 | enum class PixelFormat { | 25 | enum class PixelFormat { |
| 26 | ABGR8U = 0, | 26 | ABGR8U = 0, |
| 27 | ABGR8S = 1, | 27 | ABGR8S = 1, |
| 28 | B5G6R5 = 2, | 28 | B5G6R5U = 2, |
| 29 | A2B10G10R10 = 3, | 29 | A2B10G10R10U = 3, |
| 30 | A1B5G5R5 = 4, | 30 | A1B5G5R5U = 4, |
| 31 | R8 = 5, | 31 | R8U = 5, |
| 32 | R8UI = 6, | 32 | R8UI = 6, |
| 33 | RGBA16F = 7, | 33 | RGBA16F = 7, |
| 34 | R11FG11FB10F = 8, | 34 | RGBA16U = 8, |
| 35 | RGBA32UI = 9, | 35 | RGBA16UI = 9, |
| 36 | DXT1 = 10, | 36 | R11FG11FB10F = 10, |
| 37 | DXT23 = 11, | 37 | RGBA32UI = 11, |
| 38 | DXT45 = 12, | 38 | DXT1 = 12, |
| 39 | DXN1 = 13, // This is also known as BC4 | 39 | DXT23 = 13, |
| 40 | DXN2UNORM = 14, | 40 | DXT45 = 14, |
| 41 | DXN2SNORM = 15, | 41 | DXN1 = 15, // This is also known as BC4 |
| 42 | BC7U = 16, | 42 | DXN2UNORM = 16, |
| 43 | ASTC_2D_4X4 = 17, | 43 | DXN2SNORM = 17, |
| 44 | G8R8 = 18, | 44 | BC7U = 18, |
| 45 | BGRA8 = 19, | 45 | ASTC_2D_4X4 = 19, |
| 46 | RGBA32F = 20, | 46 | G8R8U = 20, |
| 47 | RG32F = 21, | 47 | G8R8S = 21, |
| 48 | R32F = 22, | 48 | BGRA8 = 22, |
| 49 | R16F = 23, | 49 | RGBA32F = 23, |
| 50 | R16UNORM = 24, | 50 | RG32F = 24, |
| 51 | R16S = 25, | 51 | R32F = 25, |
| 52 | R16UI = 26, | 52 | R16F = 26, |
| 53 | R16I = 27, | 53 | R16U = 27, |
| 54 | RG16 = 28, | 54 | R16S = 28, |
| 55 | RG16F = 29, | 55 | R16UI = 29, |
| 56 | RG16UI = 30, | 56 | R16I = 30, |
| 57 | RG16I = 31, | 57 | RG16 = 31, |
| 58 | RG16S = 32, | 58 | RG16F = 32, |
| 59 | RGB32F = 33, | 59 | RG16UI = 33, |
| 60 | SRGBA8 = 34, | 60 | RG16I = 34, |
| 61 | RG8S = 35, | 61 | RG16S = 35, |
| 62 | RGB32F = 36, | ||
| 63 | SRGBA8 = 37, | ||
| 64 | RG8U = 38, | ||
| 65 | RG8S = 39, | ||
| 66 | RG32UI = 40, | ||
| 67 | R32UI = 41, | ||
| 62 | 68 | ||
| 63 | MaxColorFormat, | 69 | MaxColorFormat, |
| 64 | 70 | ||
| 65 | // DepthStencil formats | 71 | // DepthStencil formats |
| 66 | Z24S8 = 36, | 72 | Z24S8 = 42, |
| 67 | S8Z24 = 37, | 73 | S8Z24 = 43, |
| 68 | Z32F = 38, | 74 | Z32F = 44, |
| 69 | Z16 = 39, | 75 | Z16 = 45, |
| 70 | Z32FS8 = 40, | 76 | Z32FS8 = 46, |
| 71 | 77 | ||
| 72 | MaxDepthStencilFormat, | 78 | MaxDepthStencilFormat, |
| 73 | 79 | ||
| @@ -107,12 +113,14 @@ struct SurfaceParams { | |||
| 107 | constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{ | 113 | constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{ |
| 108 | 1, // ABGR8U | 114 | 1, // ABGR8U |
| 109 | 1, // ABGR8S | 115 | 1, // ABGR8S |
| 110 | 1, // B5G6R5 | 116 | 1, // B5G6R5U |
| 111 | 1, // A2B10G10R10 | 117 | 1, // A2B10G10R10U |
| 112 | 1, // A1B5G5R5 | 118 | 1, // A1B5G5R5U |
| 113 | 1, // R8 | 119 | 1, // R8U |
| 114 | 1, // R8UI | 120 | 1, // R8UI |
| 115 | 1, // RGBA16F | 121 | 1, // RGBA16F |
| 122 | 1, // RGBA16U | ||
| 123 | 1, // RGBA16UI | ||
| 116 | 1, // R11FG11FB10F | 124 | 1, // R11FG11FB10F |
| 117 | 1, // RGBA32UI | 125 | 1, // RGBA32UI |
| 118 | 4, // DXT1 | 126 | 4, // DXT1 |
| @@ -123,13 +131,14 @@ struct SurfaceParams { | |||
| 123 | 4, // DXN2SNORM | 131 | 4, // DXN2SNORM |
| 124 | 4, // BC7U | 132 | 4, // BC7U |
| 125 | 4, // ASTC_2D_4X4 | 133 | 4, // ASTC_2D_4X4 |
| 126 | 1, // G8R8 | 134 | 1, // G8R8U |
| 135 | 1, // G8R8S | ||
| 127 | 1, // BGRA8 | 136 | 1, // BGRA8 |
| 128 | 1, // RGBA32F | 137 | 1, // RGBA32F |
| 129 | 1, // RG32F | 138 | 1, // RG32F |
| 130 | 1, // R32F | 139 | 1, // R32F |
| 131 | 1, // R16F | 140 | 1, // R16F |
| 132 | 1, // R16UNORM | 141 | 1, // R16U |
| 133 | 1, // R16S | 142 | 1, // R16S |
| 134 | 1, // R16UI | 143 | 1, // R16UI |
| 135 | 1, // R16I | 144 | 1, // R16I |
| @@ -140,7 +149,10 @@ struct SurfaceParams { | |||
| 140 | 1, // RG16S | 149 | 1, // RG16S |
| 141 | 1, // RGB32F | 150 | 1, // RGB32F |
| 142 | 1, // SRGBA8 | 151 | 1, // SRGBA8 |
| 152 | 1, // RG8U | ||
| 143 | 1, // RG8S | 153 | 1, // RG8S |
| 154 | 1, // RG32UI | ||
| 155 | 1, // R32UI | ||
| 144 | 1, // Z24S8 | 156 | 1, // Z24S8 |
| 145 | 1, // S8Z24 | 157 | 1, // S8Z24 |
| 146 | 1, // Z32F | 158 | 1, // Z32F |
| @@ -159,12 +171,14 @@ struct SurfaceParams { | |||
| 159 | constexpr std::array<u32, MaxPixelFormat> bpp_table = {{ | 171 | constexpr std::array<u32, MaxPixelFormat> bpp_table = {{ |
| 160 | 32, // ABGR8U | 172 | 32, // ABGR8U |
| 161 | 32, // ABGR8S | 173 | 32, // ABGR8S |
| 162 | 16, // B5G6R5 | 174 | 16, // B5G6R5U |
| 163 | 32, // A2B10G10R10 | 175 | 32, // A2B10G10R10U |
| 164 | 16, // A1B5G5R5 | 176 | 16, // A1B5G5R5U |
| 165 | 8, // R8 | 177 | 8, // R8U |
| 166 | 8, // R8UI | 178 | 8, // R8UI |
| 167 | 64, // RGBA16F | 179 | 64, // RGBA16F |
| 180 | 64, // RGBA16U | ||
| 181 | 64, // RGBA16UI | ||
| 168 | 32, // R11FG11FB10F | 182 | 32, // R11FG11FB10F |
| 169 | 128, // RGBA32UI | 183 | 128, // RGBA32UI |
| 170 | 64, // DXT1 | 184 | 64, // DXT1 |
| @@ -175,13 +189,14 @@ struct SurfaceParams { | |||
| 175 | 128, // DXN2SNORM | 189 | 128, // DXN2SNORM |
| 176 | 128, // BC7U | 190 | 128, // BC7U |
| 177 | 32, // ASTC_2D_4X4 | 191 | 32, // ASTC_2D_4X4 |
| 178 | 16, // G8R8 | 192 | 16, // G8R8U |
| 193 | 16, // G8R8S | ||
| 179 | 32, // BGRA8 | 194 | 32, // BGRA8 |
| 180 | 128, // RGBA32F | 195 | 128, // RGBA32F |
| 181 | 64, // RG32F | 196 | 64, // RG32F |
| 182 | 32, // R32F | 197 | 32, // R32F |
| 183 | 16, // R16F | 198 | 16, // R16F |
| 184 | 16, // R16UNORM | 199 | 16, // R16U |
| 185 | 16, // R16S | 200 | 16, // R16S |
| 186 | 16, // R16UI | 201 | 16, // R16UI |
| 187 | 16, // R16I | 202 | 16, // R16I |
| @@ -192,7 +207,10 @@ struct SurfaceParams { | |||
| 192 | 32, // RG16S | 207 | 32, // RG16S |
| 193 | 96, // RGB32F | 208 | 96, // RGB32F |
| 194 | 32, // SRGBA8 | 209 | 32, // SRGBA8 |
| 210 | 16, // RG8U | ||
| 195 | 16, // RG8S | 211 | 16, // RG8S |
| 212 | 64, // RG32UI | ||
| 213 | 32, // R32UI | ||
| 196 | 32, // Z24S8 | 214 | 32, // Z24S8 |
| 197 | 32, // S8Z24 | 215 | 32, // S8Z24 |
| 198 | 32, // Z32F | 216 | 32, // Z32F |
| @@ -238,9 +256,13 @@ struct SurfaceParams { | |||
| 238 | case Tegra::RenderTargetFormat::BGRA8_UNORM: | 256 | case Tegra::RenderTargetFormat::BGRA8_UNORM: |
| 239 | return PixelFormat::BGRA8; | 257 | return PixelFormat::BGRA8; |
| 240 | case Tegra::RenderTargetFormat::RGB10_A2_UNORM: | 258 | case Tegra::RenderTargetFormat::RGB10_A2_UNORM: |
| 241 | return PixelFormat::A2B10G10R10; | 259 | return PixelFormat::A2B10G10R10U; |
| 242 | case Tegra::RenderTargetFormat::RGBA16_FLOAT: | 260 | case Tegra::RenderTargetFormat::RGBA16_FLOAT: |
| 243 | return PixelFormat::RGBA16F; | 261 | return PixelFormat::RGBA16F; |
| 262 | case Tegra::RenderTargetFormat::RGBA16_UNORM: | ||
| 263 | return PixelFormat::RGBA16U; | ||
| 264 | case Tegra::RenderTargetFormat::RGBA16_UINT: | ||
| 265 | return PixelFormat::RGBA16UI; | ||
| 244 | case Tegra::RenderTargetFormat::RGBA32_FLOAT: | 266 | case Tegra::RenderTargetFormat::RGBA32_FLOAT: |
| 245 | return PixelFormat::RGBA32F; | 267 | return PixelFormat::RGBA32F; |
| 246 | case Tegra::RenderTargetFormat::RG32_FLOAT: | 268 | case Tegra::RenderTargetFormat::RG32_FLOAT: |
| @@ -248,11 +270,11 @@ struct SurfaceParams { | |||
| 248 | case Tegra::RenderTargetFormat::R11G11B10_FLOAT: | 270 | case Tegra::RenderTargetFormat::R11G11B10_FLOAT: |
| 249 | return PixelFormat::R11FG11FB10F; | 271 | return PixelFormat::R11FG11FB10F; |
| 250 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: | 272 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: |
| 251 | return PixelFormat::B5G6R5; | 273 | return PixelFormat::B5G6R5U; |
| 252 | case Tegra::RenderTargetFormat::RGBA32_UINT: | 274 | case Tegra::RenderTargetFormat::RGBA32_UINT: |
| 253 | return PixelFormat::RGBA32UI; | 275 | return PixelFormat::RGBA32UI; |
| 254 | case Tegra::RenderTargetFormat::R8_UNORM: | 276 | case Tegra::RenderTargetFormat::R8_UNORM: |
| 255 | return PixelFormat::R8; | 277 | return PixelFormat::R8U; |
| 256 | case Tegra::RenderTargetFormat::R8_UINT: | 278 | case Tegra::RenderTargetFormat::R8_UINT: |
| 257 | return PixelFormat::R8UI; | 279 | return PixelFormat::R8UI; |
| 258 | case Tegra::RenderTargetFormat::RG16_FLOAT: | 280 | case Tegra::RenderTargetFormat::RG16_FLOAT: |
| @@ -265,12 +287,14 @@ struct SurfaceParams { | |||
| 265 | return PixelFormat::RG16; | 287 | return PixelFormat::RG16; |
| 266 | case Tegra::RenderTargetFormat::RG16_SNORM: | 288 | case Tegra::RenderTargetFormat::RG16_SNORM: |
| 267 | return PixelFormat::RG16S; | 289 | return PixelFormat::RG16S; |
| 290 | case Tegra::RenderTargetFormat::RG8_UNORM: | ||
| 291 | return PixelFormat::RG8U; | ||
| 268 | case Tegra::RenderTargetFormat::RG8_SNORM: | 292 | case Tegra::RenderTargetFormat::RG8_SNORM: |
| 269 | return PixelFormat::RG8S; | 293 | return PixelFormat::RG8S; |
| 270 | case Tegra::RenderTargetFormat::R16_FLOAT: | 294 | case Tegra::RenderTargetFormat::R16_FLOAT: |
| 271 | return PixelFormat::R16F; | 295 | return PixelFormat::R16F; |
| 272 | case Tegra::RenderTargetFormat::R16_UNORM: | 296 | case Tegra::RenderTargetFormat::R16_UNORM: |
| 273 | return PixelFormat::R16UNORM; | 297 | return PixelFormat::R16U; |
| 274 | case Tegra::RenderTargetFormat::R16_SNORM: | 298 | case Tegra::RenderTargetFormat::R16_SNORM: |
| 275 | return PixelFormat::R16S; | 299 | return PixelFormat::R16S; |
| 276 | case Tegra::RenderTargetFormat::R16_UINT: | 300 | case Tegra::RenderTargetFormat::R16_UINT: |
| @@ -279,6 +303,10 @@ struct SurfaceParams { | |||
| 279 | return PixelFormat::R16I; | 303 | return PixelFormat::R16I; |
| 280 | case Tegra::RenderTargetFormat::R32_FLOAT: | 304 | case Tegra::RenderTargetFormat::R32_FLOAT: |
| 281 | return PixelFormat::R32F; | 305 | return PixelFormat::R32F; |
| 306 | case Tegra::RenderTargetFormat::R32_UINT: | ||
| 307 | return PixelFormat::R32UI; | ||
| 308 | case Tegra::RenderTargetFormat::RG32_UINT: | ||
| 309 | return PixelFormat::RG32UI; | ||
| 282 | default: | 310 | default: |
| 283 | LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); | 311 | LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); |
| 284 | UNREACHABLE(); | 312 | UNREACHABLE(); |
| @@ -300,15 +328,33 @@ struct SurfaceParams { | |||
| 300 | static_cast<u32>(component_type)); | 328 | static_cast<u32>(component_type)); |
| 301 | UNREACHABLE(); | 329 | UNREACHABLE(); |
| 302 | case Tegra::Texture::TextureFormat::B5G6R5: | 330 | case Tegra::Texture::TextureFormat::B5G6R5: |
| 303 | return PixelFormat::B5G6R5; | 331 | switch (component_type) { |
| 332 | case Tegra::Texture::ComponentType::UNORM: | ||
| 333 | return PixelFormat::B5G6R5U; | ||
| 334 | } | ||
| 335 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", | ||
| 336 | static_cast<u32>(component_type)); | ||
| 337 | UNREACHABLE(); | ||
| 304 | case Tegra::Texture::TextureFormat::A2B10G10R10: | 338 | case Tegra::Texture::TextureFormat::A2B10G10R10: |
| 305 | return PixelFormat::A2B10G10R10; | 339 | switch (component_type) { |
| 340 | case Tegra::Texture::ComponentType::UNORM: | ||
| 341 | return PixelFormat::A2B10G10R10U; | ||
| 342 | } | ||
| 343 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", | ||
| 344 | static_cast<u32>(component_type)); | ||
| 345 | UNREACHABLE(); | ||
| 306 | case Tegra::Texture::TextureFormat::A1B5G5R5: | 346 | case Tegra::Texture::TextureFormat::A1B5G5R5: |
| 307 | return PixelFormat::A1B5G5R5; | 347 | switch (component_type) { |
| 348 | case Tegra::Texture::ComponentType::UNORM: | ||
| 349 | return PixelFormat::A1B5G5R5U; | ||
| 350 | } | ||
| 351 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", | ||
| 352 | static_cast<u32>(component_type)); | ||
| 353 | UNREACHABLE(); | ||
| 308 | case Tegra::Texture::TextureFormat::R8: | 354 | case Tegra::Texture::TextureFormat::R8: |
| 309 | switch (component_type) { | 355 | switch (component_type) { |
| 310 | case Tegra::Texture::ComponentType::UNORM: | 356 | case Tegra::Texture::ComponentType::UNORM: |
| 311 | return PixelFormat::R8; | 357 | return PixelFormat::R8U; |
| 312 | case Tegra::Texture::ComponentType::UINT: | 358 | case Tegra::Texture::ComponentType::UINT: |
| 313 | return PixelFormat::R8UI; | 359 | return PixelFormat::R8UI; |
| 314 | } | 360 | } |
| @@ -316,11 +362,33 @@ struct SurfaceParams { | |||
| 316 | static_cast<u32>(component_type)); | 362 | static_cast<u32>(component_type)); |
| 317 | UNREACHABLE(); | 363 | UNREACHABLE(); |
| 318 | case Tegra::Texture::TextureFormat::G8R8: | 364 | case Tegra::Texture::TextureFormat::G8R8: |
| 319 | return PixelFormat::G8R8; | 365 | switch (component_type) { |
| 366 | case Tegra::Texture::ComponentType::UNORM: | ||
| 367 | return PixelFormat::G8R8U; | ||
| 368 | case Tegra::Texture::ComponentType::SNORM: | ||
| 369 | return PixelFormat::G8R8S; | ||
| 370 | } | ||
| 371 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", | ||
| 372 | static_cast<u32>(component_type)); | ||
| 373 | UNREACHABLE(); | ||
| 320 | case Tegra::Texture::TextureFormat::R16_G16_B16_A16: | 374 | case Tegra::Texture::TextureFormat::R16_G16_B16_A16: |
| 321 | return PixelFormat::RGBA16F; | 375 | switch (component_type) { |
| 376 | case Tegra::Texture::ComponentType::UNORM: | ||
| 377 | return PixelFormat::RGBA16U; | ||
| 378 | case Tegra::Texture::ComponentType::FLOAT: | ||
| 379 | return PixelFormat::RGBA16F; | ||
| 380 | } | ||
| 381 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", | ||
| 382 | static_cast<u32>(component_type)); | ||
| 383 | UNREACHABLE(); | ||
| 322 | case Tegra::Texture::TextureFormat::BF10GF11RF11: | 384 | case Tegra::Texture::TextureFormat::BF10GF11RF11: |
| 323 | return PixelFormat::R11FG11FB10F; | 385 | switch (component_type) { |
| 386 | case Tegra::Texture::ComponentType::FLOAT: | ||
| 387 | return PixelFormat::R11FG11FB10F; | ||
| 388 | } | ||
| 389 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", | ||
| 390 | static_cast<u32>(component_type)); | ||
| 391 | UNREACHABLE(); | ||
| 324 | case Tegra::Texture::TextureFormat::R32_G32_B32_A32: | 392 | case Tegra::Texture::TextureFormat::R32_G32_B32_A32: |
| 325 | switch (component_type) { | 393 | switch (component_type) { |
| 326 | case Tegra::Texture::ComponentType::FLOAT: | 394 | case Tegra::Texture::ComponentType::FLOAT: |
| @@ -332,15 +400,29 @@ struct SurfaceParams { | |||
| 332 | static_cast<u32>(component_type)); | 400 | static_cast<u32>(component_type)); |
| 333 | UNREACHABLE(); | 401 | UNREACHABLE(); |
| 334 | case Tegra::Texture::TextureFormat::R32_G32: | 402 | case Tegra::Texture::TextureFormat::R32_G32: |
| 335 | return PixelFormat::RG32F; | 403 | switch (component_type) { |
| 404 | case Tegra::Texture::ComponentType::FLOAT: | ||
| 405 | return PixelFormat::RG32F; | ||
| 406 | case Tegra::Texture::ComponentType::UINT: | ||
| 407 | return PixelFormat::RG32UI; | ||
| 408 | } | ||
| 409 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", | ||
| 410 | static_cast<u32>(component_type)); | ||
| 411 | UNREACHABLE(); | ||
| 336 | case Tegra::Texture::TextureFormat::R32_G32_B32: | 412 | case Tegra::Texture::TextureFormat::R32_G32_B32: |
| 337 | return PixelFormat::RGB32F; | 413 | switch (component_type) { |
| 414 | case Tegra::Texture::ComponentType::FLOAT: | ||
| 415 | return PixelFormat::RGB32F; | ||
| 416 | } | ||
| 417 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", | ||
| 418 | static_cast<u32>(component_type)); | ||
| 419 | UNREACHABLE(); | ||
| 338 | case Tegra::Texture::TextureFormat::R16: | 420 | case Tegra::Texture::TextureFormat::R16: |
| 339 | switch (component_type) { | 421 | switch (component_type) { |
| 340 | case Tegra::Texture::ComponentType::FLOAT: | 422 | case Tegra::Texture::ComponentType::FLOAT: |
| 341 | return PixelFormat::R16F; | 423 | return PixelFormat::R16F; |
| 342 | case Tegra::Texture::ComponentType::UNORM: | 424 | case Tegra::Texture::ComponentType::UNORM: |
| 343 | return PixelFormat::R16UNORM; | 425 | return PixelFormat::R16U; |
| 344 | case Tegra::Texture::ComponentType::SNORM: | 426 | case Tegra::Texture::ComponentType::SNORM: |
| 345 | return PixelFormat::R16S; | 427 | return PixelFormat::R16S; |
| 346 | case Tegra::Texture::ComponentType::UINT: | 428 | case Tegra::Texture::ComponentType::UINT: |
| @@ -352,9 +434,19 @@ struct SurfaceParams { | |||
| 352 | static_cast<u32>(component_type)); | 434 | static_cast<u32>(component_type)); |
| 353 | UNREACHABLE(); | 435 | UNREACHABLE(); |
| 354 | case Tegra::Texture::TextureFormat::R32: | 436 | case Tegra::Texture::TextureFormat::R32: |
| 355 | return PixelFormat::R32F; | 437 | switch (component_type) { |
| 438 | case Tegra::Texture::ComponentType::FLOAT: | ||
| 439 | return PixelFormat::R32F; | ||
| 440 | case Tegra::Texture::ComponentType::UINT: | ||
| 441 | return PixelFormat::R32UI; | ||
| 442 | } | ||
| 443 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", | ||
| 444 | static_cast<u32>(component_type)); | ||
| 445 | UNREACHABLE(); | ||
| 356 | case Tegra::Texture::TextureFormat::ZF32: | 446 | case Tegra::Texture::TextureFormat::ZF32: |
| 357 | return PixelFormat::Z32F; | 447 | return PixelFormat::Z32F; |
| 448 | case Tegra::Texture::TextureFormat::Z16: | ||
| 449 | return PixelFormat::Z16; | ||
| 358 | case Tegra::Texture::TextureFormat::Z24S8: | 450 | case Tegra::Texture::TextureFormat::Z24S8: |
| 359 | return PixelFormat::Z24S8; | 451 | return PixelFormat::Z24S8; |
| 360 | case Tegra::Texture::TextureFormat::DXT1: | 452 | case Tegra::Texture::TextureFormat::DXT1: |
| @@ -432,6 +524,8 @@ struct SurfaceParams { | |||
| 432 | case Tegra::RenderTargetFormat::RG16_UNORM: | 524 | case Tegra::RenderTargetFormat::RG16_UNORM: |
| 433 | case Tegra::RenderTargetFormat::R16_UNORM: | 525 | case Tegra::RenderTargetFormat::R16_UNORM: |
| 434 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: | 526 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: |
| 527 | case Tegra::RenderTargetFormat::RG8_UNORM: | ||
| 528 | case Tegra::RenderTargetFormat::RGBA16_UNORM: | ||
| 435 | return ComponentType::UNorm; | 529 | return ComponentType::UNorm; |
| 436 | case Tegra::RenderTargetFormat::RGBA8_SNORM: | 530 | case Tegra::RenderTargetFormat::RGBA8_SNORM: |
| 437 | case Tegra::RenderTargetFormat::RG16_SNORM: | 531 | case Tegra::RenderTargetFormat::RG16_SNORM: |
| @@ -447,9 +541,12 @@ struct SurfaceParams { | |||
| 447 | case Tegra::RenderTargetFormat::R32_FLOAT: | 541 | case Tegra::RenderTargetFormat::R32_FLOAT: |
| 448 | return ComponentType::Float; | 542 | return ComponentType::Float; |
| 449 | case Tegra::RenderTargetFormat::RGBA32_UINT: | 543 | case Tegra::RenderTargetFormat::RGBA32_UINT: |
| 544 | case Tegra::RenderTargetFormat::RGBA16_UINT: | ||
| 450 | case Tegra::RenderTargetFormat::RG16_UINT: | 545 | case Tegra::RenderTargetFormat::RG16_UINT: |
| 451 | case Tegra::RenderTargetFormat::R8_UINT: | 546 | case Tegra::RenderTargetFormat::R8_UINT: |
| 452 | case Tegra::RenderTargetFormat::R16_UINT: | 547 | case Tegra::RenderTargetFormat::R16_UINT: |
| 548 | case Tegra::RenderTargetFormat::RG32_UINT: | ||
| 549 | case Tegra::RenderTargetFormat::R32_UINT: | ||
| 453 | return ComponentType::UInt; | 550 | return ComponentType::UInt; |
| 454 | case Tegra::RenderTargetFormat::RG16_SINT: | 551 | case Tegra::RenderTargetFormat::RG16_SINT: |
| 455 | case Tegra::RenderTargetFormat::R16_SINT: | 552 | case Tegra::RenderTargetFormat::R16_SINT: |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 85297bd00..e0dfdbb9f 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -356,40 +356,43 @@ public: | |||
| 356 | * @param reg The register to use as the source value. | 356 | * @param reg The register to use as the source value. |
| 357 | */ | 357 | */ |
| 358 | void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { | 358 | void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { |
| 359 | std::string dest = GetOutputAttribute(attribute) + GetSwizzle(elem); | 359 | std::string dest = GetOutputAttribute(attribute); |
| 360 | std::string src = GetRegisterAsFloat(reg); | 360 | std::string src = GetRegisterAsFloat(reg); |
| 361 | 361 | ||
| 362 | if (!dest.empty()) { | 362 | if (!dest.empty()) { |
| 363 | // Can happen with unknown/unimplemented output attributes, in which case we ignore the | 363 | // Can happen with unknown/unimplemented output attributes, in which case we ignore the |
| 364 | // instruction for now. | 364 | // instruction for now. |
| 365 | shader.AddLine(dest + " = " + src + ';'); | 365 | shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); |
| 366 | } | 366 | } |
| 367 | } | 367 | } |
| 368 | 368 | ||
| 369 | /// Generates code representing a uniform (C buffer) register, interpreted as the input type. | 369 | /// Generates code representing a uniform (C buffer) register, interpreted as the input type. |
| 370 | std::string GetUniform(u64 index, u64 offset, GLSLRegister::Type type) { | 370 | std::string GetUniform(u64 index, u64 offset, GLSLRegister::Type type, |
| 371 | Register::Size size = Register::Size::Word) { | ||
| 371 | declr_const_buffers[index].MarkAsUsed(index, offset, stage); | 372 | declr_const_buffers[index].MarkAsUsed(index, offset, stage); |
| 372 | std::string value = 'c' + std::to_string(index) + '[' + std::to_string(offset / 4) + "][" + | 373 | std::string value = 'c' + std::to_string(index) + '[' + std::to_string(offset / 4) + "][" + |
| 373 | std::to_string(offset % 4) + ']'; | 374 | std::to_string(offset % 4) + ']'; |
| 374 | 375 | ||
| 375 | if (type == GLSLRegister::Type::Float) { | 376 | if (type == GLSLRegister::Type::Float) { |
| 376 | return value; | 377 | // Do nothing, default |
| 377 | } else if (type == GLSLRegister::Type::Integer) { | 378 | } else if (type == GLSLRegister::Type::Integer) { |
| 378 | return "floatBitsToInt(" + value + ')'; | 379 | value = "floatBitsToInt(" + value + ')'; |
| 380 | } else if (type == GLSLRegister::Type::UnsignedInteger) { | ||
| 381 | value = "floatBitsToUint(" + value + ')'; | ||
| 379 | } else { | 382 | } else { |
| 380 | UNREACHABLE(); | 383 | UNREACHABLE(); |
| 381 | } | 384 | } |
| 385 | |||
| 386 | return ConvertIntegerSize(value, size); | ||
| 382 | } | 387 | } |
| 383 | 388 | ||
| 384 | std::string GetUniformIndirect(u64 index, s64 offset, const Register& index_reg, | 389 | std::string GetUniformIndirect(u64 cbuf_index, s64 offset, const std::string& index_str, |
| 385 | GLSLRegister::Type type) { | 390 | GLSLRegister::Type type) { |
| 386 | declr_const_buffers[index].MarkAsUsedIndirect(index, stage); | 391 | declr_const_buffers[cbuf_index].MarkAsUsedIndirect(cbuf_index, stage); |
| 387 | |||
| 388 | std::string final_offset = "((floatBitsToInt(" + GetRegister(index_reg, 0) + ") + " + | ||
| 389 | std::to_string(offset) + ") / 4)"; | ||
| 390 | 392 | ||
| 391 | std::string value = | 393 | std::string final_offset = fmt::format("({} + {})", index_str, offset / 4); |
| 392 | 'c' + std::to_string(index) + '[' + final_offset + " / 4][" + final_offset + " % 4]"; | 394 | std::string value = 'c' + std::to_string(cbuf_index) + '[' + final_offset + " / 4][" + |
| 395 | final_offset + " % 4]"; | ||
| 393 | 396 | ||
| 394 | if (type == GLSLRegister::Type::Float) { | 397 | if (type == GLSLRegister::Type::Float) { |
| 395 | return value; | 398 | return value; |
| @@ -1247,20 +1250,41 @@ private: | |||
| 1247 | op_a = "abs(" + op_a + ')'; | 1250 | op_a = "abs(" + op_a + ')'; |
| 1248 | } | 1251 | } |
| 1249 | 1252 | ||
| 1253 | if (instr.conversion.negate_a) { | ||
| 1254 | op_a = "-(" + op_a + ')'; | ||
| 1255 | } | ||
| 1256 | |||
| 1250 | regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, | 1257 | regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, |
| 1251 | 1, instr.alu.saturate_d, 0, instr.conversion.dest_size); | 1258 | 1, instr.alu.saturate_d, 0, instr.conversion.dest_size); |
| 1252 | break; | 1259 | break; |
| 1253 | } | 1260 | } |
| 1254 | case OpCode::Id::I2F_R: { | 1261 | case OpCode::Id::I2F_R: |
| 1262 | case OpCode::Id::I2F_C: { | ||
| 1255 | ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); | 1263 | ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); |
| 1256 | ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); | 1264 | ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); |
| 1257 | std::string op_a = regs.GetRegisterAsInteger( | 1265 | |
| 1258 | instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size); | 1266 | std::string op_a{}; |
| 1267 | |||
| 1268 | if (instr.is_b_gpr) { | ||
| 1269 | op_a = | ||
| 1270 | regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed, | ||
| 1271 | instr.conversion.src_size); | ||
| 1272 | } else { | ||
| 1273 | op_a = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, | ||
| 1274 | instr.conversion.is_input_signed | ||
| 1275 | ? GLSLRegister::Type::Integer | ||
| 1276 | : GLSLRegister::Type::UnsignedInteger, | ||
| 1277 | instr.conversion.src_size); | ||
| 1278 | } | ||
| 1259 | 1279 | ||
| 1260 | if (instr.conversion.abs_a) { | 1280 | if (instr.conversion.abs_a) { |
| 1261 | op_a = "abs(" + op_a + ')'; | 1281 | op_a = "abs(" + op_a + ')'; |
| 1262 | } | 1282 | } |
| 1263 | 1283 | ||
| 1284 | if (instr.conversion.negate_a) { | ||
| 1285 | op_a = "-(" + op_a + ')'; | ||
| 1286 | } | ||
| 1287 | |||
| 1264 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); | 1288 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); |
| 1265 | break; | 1289 | break; |
| 1266 | } | 1290 | } |
| @@ -1269,6 +1293,14 @@ private: | |||
| 1269 | ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); | 1293 | ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); |
| 1270 | std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); | 1294 | std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); |
| 1271 | 1295 | ||
| 1296 | if (instr.conversion.abs_a) { | ||
| 1297 | op_a = "abs(" + op_a + ')'; | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | if (instr.conversion.negate_a) { | ||
| 1301 | op_a = "-(" + op_a + ')'; | ||
| 1302 | } | ||
| 1303 | |||
| 1272 | switch (instr.conversion.f2f.rounding) { | 1304 | switch (instr.conversion.f2f.rounding) { |
| 1273 | case Tegra::Shader::F2fRoundingOp::None: | 1305 | case Tegra::Shader::F2fRoundingOp::None: |
| 1274 | break; | 1306 | break; |
| @@ -1291,21 +1323,29 @@ private: | |||
| 1291 | break; | 1323 | break; |
| 1292 | } | 1324 | } |
| 1293 | 1325 | ||
| 1294 | if (instr.conversion.abs_a) { | ||
| 1295 | op_a = "abs(" + op_a + ')'; | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); | 1326 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); |
| 1299 | break; | 1327 | break; |
| 1300 | } | 1328 | } |
| 1301 | case OpCode::Id::F2I_R: { | 1329 | case OpCode::Id::F2I_R: |
| 1330 | case OpCode::Id::F2I_C: { | ||
| 1302 | ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); | 1331 | ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); |
| 1303 | std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); | 1332 | std::string op_a{}; |
| 1333 | |||
| 1334 | if (instr.is_b_gpr) { | ||
| 1335 | op_a = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 1336 | } else { | ||
| 1337 | op_a = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, | ||
| 1338 | GLSLRegister::Type::Float); | ||
| 1339 | } | ||
| 1304 | 1340 | ||
| 1305 | if (instr.conversion.abs_a) { | 1341 | if (instr.conversion.abs_a) { |
| 1306 | op_a = "abs(" + op_a + ')'; | 1342 | op_a = "abs(" + op_a + ')'; |
| 1307 | } | 1343 | } |
| 1308 | 1344 | ||
| 1345 | if (instr.conversion.negate_a) { | ||
| 1346 | op_a = "-(" + op_a + ')'; | ||
| 1347 | } | ||
| 1348 | |||
| 1309 | switch (instr.conversion.f2i.rounding) { | 1349 | switch (instr.conversion.f2i.rounding) { |
| 1310 | case Tegra::Shader::F2iRoundingOp::None: | 1350 | case Tegra::Shader::F2iRoundingOp::None: |
| 1311 | break; | 1351 | break; |
| @@ -1353,11 +1393,16 @@ private: | |||
| 1353 | case OpCode::Id::LD_C: { | 1393 | case OpCode::Id::LD_C: { |
| 1354 | ASSERT_MSG(instr.ld_c.unknown == 0, "Unimplemented"); | 1394 | ASSERT_MSG(instr.ld_c.unknown == 0, "Unimplemented"); |
| 1355 | 1395 | ||
| 1396 | // Add an extra scope and declare the index register inside to prevent | ||
| 1397 | // overwriting it in case it is used as an output of the LD instruction. | ||
| 1398 | shader.AddLine("{"); | ||
| 1399 | ++shader.scope; | ||
| 1400 | |||
| 1401 | shader.AddLine("uint index = (" + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + | ||
| 1402 | " / 4) & (MAX_CONSTBUFFER_ELEMENTS - 1);"); | ||
| 1403 | |||
| 1356 | std::string op_a = | 1404 | std::string op_a = |
| 1357 | regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 0, instr.gpr8, | 1405 | regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 0, "index", |
| 1358 | GLSLRegister::Type::Float); | ||
| 1359 | std::string op_b = | ||
| 1360 | regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 4, instr.gpr8, | ||
| 1361 | GLSLRegister::Type::Float); | 1406 | GLSLRegister::Type::Float); |
| 1362 | 1407 | ||
| 1363 | switch (instr.ld_c.type.Value()) { | 1408 | switch (instr.ld_c.type.Value()) { |
| @@ -1365,16 +1410,22 @@ private: | |||
| 1365 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); | 1410 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); |
| 1366 | break; | 1411 | break; |
| 1367 | 1412 | ||
| 1368 | case Tegra::Shader::UniformType::Double: | 1413 | case Tegra::Shader::UniformType::Double: { |
| 1414 | std::string op_b = | ||
| 1415 | regs.GetUniformIndirect(instr.cbuf36.index, instr.cbuf36.offset + 4, | ||
| 1416 | "index", GLSLRegister::Type::Float); | ||
| 1369 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); | 1417 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); |
| 1370 | regs.SetRegisterToFloat(instr.gpr0.Value() + 1, 0, op_b, 1, 1); | 1418 | regs.SetRegisterToFloat(instr.gpr0.Value() + 1, 0, op_b, 1, 1); |
| 1371 | break; | 1419 | break; |
| 1372 | 1420 | } | |
| 1373 | default: | 1421 | default: |
| 1374 | LOG_CRITICAL(HW_GPU, "Unhandled type: {}", | 1422 | LOG_CRITICAL(HW_GPU, "Unhandled type: {}", |
| 1375 | static_cast<unsigned>(instr.ld_c.type.Value())); | 1423 | static_cast<unsigned>(instr.ld_c.type.Value())); |
| 1376 | UNREACHABLE(); | 1424 | UNREACHABLE(); |
| 1377 | } | 1425 | } |
| 1426 | |||
| 1427 | --shader.scope; | ||
| 1428 | shader.AddLine("}"); | ||
| 1378 | break; | 1429 | break; |
| 1379 | } | 1430 | } |
| 1380 | case OpCode::Id::ST_A: { | 1431 | case OpCode::Id::ST_A: { |
| @@ -1630,6 +1681,99 @@ private: | |||
| 1630 | } | 1681 | } |
| 1631 | break; | 1682 | break; |
| 1632 | } | 1683 | } |
| 1684 | case OpCode::Type::Xmad: { | ||
| 1685 | ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); | ||
| 1686 | ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); | ||
| 1687 | |||
| 1688 | std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)}; | ||
| 1689 | std::string op_b; | ||
| 1690 | std::string op_c; | ||
| 1691 | |||
| 1692 | // TODO(bunnei): Needs to be fixed once op_a or op_b is signed | ||
| 1693 | ASSERT_MSG(instr.xmad.sign_a == instr.xmad.sign_b, "Unimplemented"); | ||
| 1694 | const bool is_signed{instr.xmad.sign_a == 1}; | ||
| 1695 | |||
| 1696 | bool is_merge{}; | ||
| 1697 | switch (opcode->GetId()) { | ||
| 1698 | case OpCode::Id::XMAD_CR: { | ||
| 1699 | is_merge = instr.xmad.merge_56; | ||
| 1700 | op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, | ||
| 1701 | instr.xmad.sign_b ? GLSLRegister::Type::Integer | ||
| 1702 | : GLSLRegister::Type::UnsignedInteger); | ||
| 1703 | op_c += regs.GetRegisterAsInteger(instr.gpr39, 0, is_signed); | ||
| 1704 | break; | ||
| 1705 | } | ||
| 1706 | case OpCode::Id::XMAD_RR: { | ||
| 1707 | is_merge = instr.xmad.merge_37; | ||
| 1708 | op_b += regs.GetRegisterAsInteger(instr.gpr20, 0, instr.xmad.sign_b); | ||
| 1709 | op_c += regs.GetRegisterAsInteger(instr.gpr39, 0, is_signed); | ||
| 1710 | break; | ||
| 1711 | } | ||
| 1712 | case OpCode::Id::XMAD_RC: { | ||
| 1713 | op_b += regs.GetRegisterAsInteger(instr.gpr39, 0, instr.xmad.sign_b); | ||
| 1714 | op_c += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, | ||
| 1715 | is_signed ? GLSLRegister::Type::Integer | ||
| 1716 | : GLSLRegister::Type::UnsignedInteger); | ||
| 1717 | break; | ||
| 1718 | } | ||
| 1719 | case OpCode::Id::XMAD_IMM: { | ||
| 1720 | is_merge = instr.xmad.merge_37; | ||
| 1721 | op_b += std::to_string(instr.xmad.imm20_16); | ||
| 1722 | op_c += regs.GetRegisterAsInteger(instr.gpr39, 0, is_signed); | ||
| 1723 | break; | ||
| 1724 | } | ||
| 1725 | default: { | ||
| 1726 | LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->GetName()); | ||
| 1727 | UNREACHABLE(); | ||
| 1728 | } | ||
| 1729 | } | ||
| 1730 | |||
| 1731 | // TODO(bunnei): Ensure this is right with signed operands | ||
| 1732 | if (instr.xmad.high_a) { | ||
| 1733 | op_a = "((" + op_a + ") >> 16)"; | ||
| 1734 | } else { | ||
| 1735 | op_a = "((" + op_a + ") & 0xFFFF)"; | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | std::string src2 = '(' + op_b + ')'; // Preserve original source 2 | ||
| 1739 | if (instr.xmad.high_b) { | ||
| 1740 | op_b = '(' + src2 + " >> 16)"; | ||
| 1741 | } else { | ||
| 1742 | op_b = '(' + src2 + " & 0xFFFF)"; | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | std::string product = '(' + op_a + " * " + op_b + ')'; | ||
| 1746 | if (instr.xmad.product_shift_left) { | ||
| 1747 | product = '(' + product + " << 16)"; | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | switch (instr.xmad.mode) { | ||
| 1751 | case Tegra::Shader::XmadMode::None: | ||
| 1752 | break; | ||
| 1753 | case Tegra::Shader::XmadMode::CLo: | ||
| 1754 | op_c = "((" + op_c + ") & 0xFFFF)"; | ||
| 1755 | break; | ||
| 1756 | case Tegra::Shader::XmadMode::CHi: | ||
| 1757 | op_c = "((" + op_c + ") >> 16)"; | ||
| 1758 | break; | ||
| 1759 | case Tegra::Shader::XmadMode::CBcc: | ||
| 1760 | op_c = "((" + op_c + ") + (" + src2 + "<< 16))"; | ||
| 1761 | break; | ||
| 1762 | default: { | ||
| 1763 | LOG_CRITICAL(HW_GPU, "Unhandled XMAD mode: {}", | ||
| 1764 | static_cast<u32>(instr.xmad.mode.Value())); | ||
| 1765 | UNREACHABLE(); | ||
| 1766 | } | ||
| 1767 | } | ||
| 1768 | |||
| 1769 | std::string sum{'(' + product + " + " + op_c + ')'}; | ||
| 1770 | if (is_merge) { | ||
| 1771 | sum = "((" + sum + " & 0xFFFF) | (" + src2 + "<< 16))"; | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); | ||
| 1775 | break; | ||
| 1776 | } | ||
| 1633 | default: { | 1777 | default: { |
| 1634 | switch (opcode->GetId()) { | 1778 | switch (opcode->GetId()) { |
| 1635 | case OpCode::Id::EXIT: { | 1779 | case OpCode::Id::EXIT: { |
| @@ -1667,7 +1811,15 @@ private: | |||
| 1667 | } | 1811 | } |
| 1668 | case OpCode::Id::KIL: { | 1812 | case OpCode::Id::KIL: { |
| 1669 | ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); | 1813 | ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); |
| 1814 | |||
| 1815 | // Enclose "discard" in a conditional, so that GLSL compilation does not complain | ||
| 1816 | // about unexecuted instructions that may follow this. | ||
| 1817 | shader.AddLine("if (true) {"); | ||
| 1818 | ++shader.scope; | ||
| 1670 | shader.AddLine("discard;"); | 1819 | shader.AddLine("discard;"); |
| 1820 | --shader.scope; | ||
| 1821 | shader.AddLine("}"); | ||
| 1822 | |||
| 1671 | break; | 1823 | break; |
| 1672 | } | 1824 | } |
| 1673 | case OpCode::Id::BRA: { | 1825 | case OpCode::Id::BRA: { |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 68bacd4c5..1d1975179 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -203,21 +203,6 @@ void OpenGLState::Apply() const { | |||
| 203 | } | 203 | } |
| 204 | } | 204 | } |
| 205 | 205 | ||
| 206 | // Constbuffers | ||
| 207 | for (std::size_t stage = 0; stage < draw.const_buffers.size(); ++stage) { | ||
| 208 | for (std::size_t buffer_id = 0; buffer_id < draw.const_buffers[stage].size(); ++buffer_id) { | ||
| 209 | const auto& current = cur_state.draw.const_buffers[stage][buffer_id]; | ||
| 210 | const auto& new_state = draw.const_buffers[stage][buffer_id]; | ||
| 211 | |||
| 212 | if (current.enabled != new_state.enabled || current.bindpoint != new_state.bindpoint || | ||
| 213 | current.ssbo != new_state.ssbo) { | ||
| 214 | if (new_state.enabled) { | ||
| 215 | glBindBufferBase(GL_UNIFORM_BUFFER, new_state.bindpoint, new_state.ssbo); | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | // Framebuffer | 206 | // Framebuffer |
| 222 | if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { | 207 | if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { |
| 223 | glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); | 208 | glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 5c7b636e4..bdb02ba25 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -119,12 +119,6 @@ public: | |||
| 119 | GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING | 119 | GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING |
| 120 | GLuint shader_program; // GL_CURRENT_PROGRAM | 120 | GLuint shader_program; // GL_CURRENT_PROGRAM |
| 121 | GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING | 121 | GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING |
| 122 | struct ConstBufferConfig { | ||
| 123 | bool enabled = false; | ||
| 124 | GLuint bindpoint; | ||
| 125 | GLuint ssbo; | ||
| 126 | }; | ||
| 127 | std::array<std::array<ConstBufferConfig, Regs::MaxConstBuffers>, 5> const_buffers; | ||
| 128 | } draw; | 122 | } draw; |
| 129 | 123 | ||
| 130 | struct { | 124 | struct { |
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index a2713e9f0..03a8ed8b7 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp | |||
| @@ -9,174 +9,91 @@ | |||
| 9 | #include "video_core/renderer_opengl/gl_state.h" | 9 | #include "video_core/renderer_opengl/gl_state.h" |
| 10 | #include "video_core/renderer_opengl/gl_stream_buffer.h" | 10 | #include "video_core/renderer_opengl/gl_stream_buffer.h" |
| 11 | 11 | ||
| 12 | class OrphanBuffer : public OGLStreamBuffer { | 12 | OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent) |
| 13 | public: | 13 | : gl_target(target), buffer_size(size) { |
| 14 | explicit OrphanBuffer(GLenum target) : OGLStreamBuffer(target) {} | 14 | gl_buffer.Create(); |
| 15 | ~OrphanBuffer() override; | 15 | glBindBuffer(gl_target, gl_buffer.handle); |
| 16 | |||
| 17 | private: | ||
| 18 | void Create(size_t size, size_t sync_subdivide) override; | ||
| 19 | void Release() override; | ||
| 20 | |||
| 21 | std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override; | ||
| 22 | void Unmap() override; | ||
| 23 | |||
| 24 | std::vector<u8> data; | ||
| 25 | }; | ||
| 26 | |||
| 27 | class StorageBuffer : public OGLStreamBuffer { | ||
| 28 | public: | ||
| 29 | explicit StorageBuffer(GLenum target) : OGLStreamBuffer(target) {} | ||
| 30 | ~StorageBuffer() override; | ||
| 31 | |||
| 32 | private: | ||
| 33 | void Create(size_t size, size_t sync_subdivide) override; | ||
| 34 | void Release() override; | ||
| 35 | |||
| 36 | std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override; | ||
| 37 | void Unmap() override; | ||
| 38 | |||
| 39 | struct Fence { | ||
| 40 | OGLSync sync; | ||
| 41 | size_t offset; | ||
| 42 | }; | ||
| 43 | std::deque<Fence> head; | ||
| 44 | std::deque<Fence> tail; | ||
| 45 | |||
| 46 | u8* mapped_ptr; | ||
| 47 | }; | ||
| 48 | |||
| 49 | OGLStreamBuffer::OGLStreamBuffer(GLenum target) { | ||
| 50 | gl_target = target; | ||
| 51 | } | ||
| 52 | |||
| 53 | GLuint OGLStreamBuffer::GetHandle() const { | ||
| 54 | return gl_buffer.handle; | ||
| 55 | } | ||
| 56 | 16 | ||
| 57 | std::unique_ptr<OGLStreamBuffer> OGLStreamBuffer::MakeBuffer(bool storage_buffer, GLenum target) { | 17 | GLsizeiptr allocate_size = size; |
| 58 | if (storage_buffer) { | 18 | if (target == GL_ARRAY_BUFFER) { |
| 59 | return std::make_unique<StorageBuffer>(target); | 19 | // On AMD GPU there is a strange crash in indexed drawing. The crash happens when the buffer |
| 20 | // read position is near the end and is an out-of-bound access to the vertex buffer. This is | ||
| 21 | // probably a bug in the driver and is related to the usage of vec3<byte> attributes in the | ||
| 22 | // vertex array. Doubling the allocation size for the vertex buffer seems to avoid the | ||
| 23 | // crash. | ||
| 24 | allocate_size *= 2; | ||
| 60 | } | 25 | } |
| 61 | return std::make_unique<OrphanBuffer>(target); | ||
| 62 | } | ||
| 63 | 26 | ||
| 64 | OrphanBuffer::~OrphanBuffer() { | 27 | if (GLAD_GL_ARB_buffer_storage) { |
| 65 | Release(); | 28 | persistent = true; |
| 29 | coherent = prefer_coherent; | ||
| 30 | GLbitfield flags = | ||
| 31 | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); | ||
| 32 | glBufferStorage(gl_target, allocate_size, nullptr, flags); | ||
| 33 | mapped_ptr = static_cast<u8*>(glMapBufferRange( | ||
| 34 | gl_target, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT))); | ||
| 35 | } else { | ||
| 36 | glBufferData(gl_target, allocate_size, nullptr, GL_STREAM_DRAW); | ||
| 37 | } | ||
| 66 | } | 38 | } |
| 67 | 39 | ||
| 68 | void OrphanBuffer::Create(size_t size, size_t /*sync_subdivide*/) { | 40 | OGLStreamBuffer::~OGLStreamBuffer() { |
| 69 | buffer_pos = 0; | 41 | if (persistent) { |
| 70 | buffer_size = size; | ||
| 71 | data.resize(buffer_size); | ||
| 72 | |||
| 73 | if (gl_buffer.handle == 0) { | ||
| 74 | gl_buffer.Create(); | ||
| 75 | glBindBuffer(gl_target, gl_buffer.handle); | 42 | glBindBuffer(gl_target, gl_buffer.handle); |
| 43 | glUnmapBuffer(gl_target); | ||
| 76 | } | 44 | } |
| 77 | |||
| 78 | glBufferData(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr, GL_STREAM_DRAW); | ||
| 79 | } | ||
| 80 | |||
| 81 | void OrphanBuffer::Release() { | ||
| 82 | gl_buffer.Release(); | 45 | gl_buffer.Release(); |
| 83 | } | 46 | } |
| 84 | 47 | ||
| 85 | std::pair<u8*, GLintptr> OrphanBuffer::Map(size_t size, size_t alignment) { | 48 | GLuint OGLStreamBuffer::GetHandle() const { |
| 86 | buffer_pos = Common::AlignUp(buffer_pos, alignment); | 49 | return gl_buffer.handle; |
| 87 | |||
| 88 | if (buffer_pos + size > buffer_size) { | ||
| 89 | Create(std::max(buffer_size, size), 0); | ||
| 90 | } | ||
| 91 | |||
| 92 | mapped_size = size; | ||
| 93 | return std::make_pair(&data[buffer_pos], static_cast<GLintptr>(buffer_pos)); | ||
| 94 | } | ||
| 95 | |||
| 96 | void OrphanBuffer::Unmap() { | ||
| 97 | glBufferSubData(gl_target, static_cast<GLintptr>(buffer_pos), | ||
| 98 | static_cast<GLsizeiptr>(mapped_size), &data[buffer_pos]); | ||
| 99 | buffer_pos += mapped_size; | ||
| 100 | } | ||
| 101 | |||
| 102 | StorageBuffer::~StorageBuffer() { | ||
| 103 | Release(); | ||
| 104 | } | 50 | } |
| 105 | 51 | ||
| 106 | void StorageBuffer::Create(size_t size, size_t sync_subdivide) { | 52 | GLsizeiptr OGLStreamBuffer::GetSize() const { |
| 107 | if (gl_buffer.handle != 0) | 53 | return buffer_size; |
| 108 | return; | ||
| 109 | |||
| 110 | buffer_pos = 0; | ||
| 111 | buffer_size = size; | ||
| 112 | buffer_sync_subdivide = std::max<size_t>(sync_subdivide, 1); | ||
| 113 | |||
| 114 | gl_buffer.Create(); | ||
| 115 | glBindBuffer(gl_target, gl_buffer.handle); | ||
| 116 | |||
| 117 | glBufferStorage(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr, | ||
| 118 | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT); | ||
| 119 | mapped_ptr = reinterpret_cast<u8*>( | ||
| 120 | glMapBufferRange(gl_target, 0, static_cast<GLsizeiptr>(buffer_size), | ||
| 121 | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT)); | ||
| 122 | } | 54 | } |
| 123 | 55 | ||
| 124 | void StorageBuffer::Release() { | 56 | std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) { |
| 125 | if (gl_buffer.handle == 0) | ||
| 126 | return; | ||
| 127 | |||
| 128 | glUnmapBuffer(gl_target); | ||
| 129 | |||
| 130 | gl_buffer.Release(); | ||
| 131 | head.clear(); | ||
| 132 | tail.clear(); | ||
| 133 | } | ||
| 134 | |||
| 135 | std::pair<u8*, GLintptr> StorageBuffer::Map(size_t size, size_t alignment) { | ||
| 136 | ASSERT(size <= buffer_size); | 57 | ASSERT(size <= buffer_size); |
| 58 | ASSERT(alignment <= buffer_size); | ||
| 59 | mapped_size = size; | ||
| 137 | 60 | ||
| 138 | OGLSync sync; | 61 | if (alignment > 0) { |
| 139 | 62 | buffer_pos = Common::AlignUp<size_t>(buffer_pos, alignment); | |
| 140 | buffer_pos = Common::AlignUp(buffer_pos, alignment); | ||
| 141 | size_t effective_offset = Common::AlignDown(buffer_pos, buffer_sync_subdivide); | ||
| 142 | |||
| 143 | if (!head.empty() && | ||
| 144 | (effective_offset > head.back().offset || buffer_pos + size > buffer_size)) { | ||
| 145 | ASSERT(head.back().sync.handle == 0); | ||
| 146 | head.back().sync.Create(); | ||
| 147 | } | 63 | } |
| 148 | 64 | ||
| 65 | bool invalidate = false; | ||
| 149 | if (buffer_pos + size > buffer_size) { | 66 | if (buffer_pos + size > buffer_size) { |
| 150 | if (!tail.empty()) { | ||
| 151 | std::swap(sync, tail.back().sync); | ||
| 152 | tail.clear(); | ||
| 153 | } | ||
| 154 | std::swap(tail, head); | ||
| 155 | buffer_pos = 0; | 67 | buffer_pos = 0; |
| 156 | effective_offset = 0; | 68 | invalidate = true; |
| 157 | } | ||
| 158 | 69 | ||
| 159 | while (!tail.empty() && buffer_pos + size > tail.front().offset) { | 70 | if (persistent) { |
| 160 | std::swap(sync, tail.front().sync); | 71 | glUnmapBuffer(gl_target); |
| 161 | tail.pop_front(); | 72 | } |
| 162 | } | 73 | } |
| 163 | 74 | ||
| 164 | if (sync.handle != 0) { | 75 | if (invalidate | !persistent) { |
| 165 | glClientWaitSync(sync.handle, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); | 76 | GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | |
| 166 | sync.Release(); | 77 | (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | |
| 78 | (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); | ||
| 79 | mapped_ptr = static_cast<u8*>( | ||
| 80 | glMapBufferRange(gl_target, buffer_pos, buffer_size - buffer_pos, flags)); | ||
| 81 | mapped_offset = buffer_pos; | ||
| 167 | } | 82 | } |
| 168 | 83 | ||
| 169 | if (head.empty() || effective_offset > head.back().offset) { | 84 | return std::make_tuple(mapped_ptr + buffer_pos - mapped_offset, buffer_pos, invalidate); |
| 170 | head.emplace_back(); | 85 | } |
| 171 | head.back().offset = effective_offset; | 86 | |
| 87 | void OGLStreamBuffer::Unmap(GLsizeiptr size) { | ||
| 88 | ASSERT(size <= mapped_size); | ||
| 89 | |||
| 90 | if (!coherent && size > 0) { | ||
| 91 | glFlushMappedBufferRange(gl_target, buffer_pos - mapped_offset, size); | ||
| 172 | } | 92 | } |
| 173 | 93 | ||
| 174 | mapped_size = size; | 94 | if (!persistent) { |
| 175 | return std::make_pair(&mapped_ptr[buffer_pos], static_cast<GLintptr>(buffer_pos)); | 95 | glUnmapBuffer(gl_target); |
| 176 | } | 96 | } |
| 177 | 97 | ||
| 178 | void StorageBuffer::Unmap() { | 98 | buffer_pos += size; |
| 179 | glFlushMappedBufferRange(gl_target, static_cast<GLintptr>(buffer_pos), | ||
| 180 | static_cast<GLsizeiptr>(mapped_size)); | ||
| 181 | buffer_pos += mapped_size; | ||
| 182 | } | 99 | } |
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h index e78dc5784..45592daaf 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.h +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h | |||
| @@ -2,35 +2,41 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #include <tuple> |
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <glad/glad.h> | 6 | #include <glad/glad.h> |
| 9 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 10 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 8 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 11 | 9 | ||
| 12 | class OGLStreamBuffer : private NonCopyable { | 10 | class OGLStreamBuffer : private NonCopyable { |
| 13 | public: | 11 | public: |
| 14 | explicit OGLStreamBuffer(GLenum target); | 12 | explicit OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent = false); |
| 15 | virtual ~OGLStreamBuffer() = default; | 13 | ~OGLStreamBuffer(); |
| 16 | |||
| 17 | public: | ||
| 18 | static std::unique_ptr<OGLStreamBuffer> MakeBuffer(bool storage_buffer, GLenum target); | ||
| 19 | |||
| 20 | virtual void Create(size_t size, size_t sync_subdivide) = 0; | ||
| 21 | virtual void Release() {} | ||
| 22 | 14 | ||
| 23 | GLuint GetHandle() const; | 15 | GLuint GetHandle() const; |
| 16 | GLsizeiptr GetSize() const; | ||
| 17 | |||
| 18 | /* | ||
| 19 | * Allocates a linear chunk of memory in the GPU buffer with at least "size" bytes | ||
| 20 | * and the optional alignment requirement. | ||
| 21 | * If the buffer is full, the whole buffer is reallocated which invalidates old chunks. | ||
| 22 | * The return values are the pointer to the new chunk, the offset within the buffer, | ||
| 23 | * and the invalidation flag for previous chunks. | ||
| 24 | * The actual used size must be specified on unmapping the chunk. | ||
| 25 | */ | ||
| 26 | std::tuple<u8*, GLintptr, bool> Map(GLsizeiptr size, GLintptr alignment = 0); | ||
| 24 | 27 | ||
| 25 | virtual std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) = 0; | 28 | void Unmap(GLsizeiptr size); |
| 26 | virtual void Unmap() = 0; | ||
| 27 | 29 | ||
| 28 | protected: | 30 | private: |
| 29 | OGLBuffer gl_buffer; | 31 | OGLBuffer gl_buffer; |
| 30 | GLenum gl_target; | 32 | GLenum gl_target; |
| 31 | 33 | ||
| 32 | size_t buffer_pos = 0; | 34 | bool coherent = false; |
| 33 | size_t buffer_size = 0; | 35 | bool persistent = false; |
| 34 | size_t buffer_sync_subdivide = 0; | 36 | |
| 35 | size_t mapped_size = 0; | 37 | GLintptr buffer_pos = 0; |
| 38 | GLsizeiptr buffer_size = 0; | ||
| 39 | GLintptr mapped_offset = 0; | ||
| 40 | GLsizeiptr mapped_size = 0; | ||
| 41 | u8* mapped_ptr = nullptr; | ||
| 36 | }; | 42 | }; |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 5afd20dbe..8f719fdd8 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -24,15 +24,25 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs; | |||
| 24 | 24 | ||
| 25 | inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | 25 | inline GLenum VertexType(Maxwell::VertexAttribute attrib) { |
| 26 | switch (attrib.type) { | 26 | switch (attrib.type) { |
| 27 | case Maxwell::VertexAttribute::Type::UnsignedInt: | ||
| 27 | case Maxwell::VertexAttribute::Type::UnsignedNorm: { | 28 | case Maxwell::VertexAttribute::Type::UnsignedNorm: { |
| 28 | 29 | ||
| 29 | switch (attrib.size) { | 30 | switch (attrib.size) { |
| 31 | case Maxwell::VertexAttribute::Size::Size_8: | ||
| 30 | case Maxwell::VertexAttribute::Size::Size_8_8: | 32 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 33 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | ||
| 31 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 34 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 32 | return GL_UNSIGNED_BYTE; | 35 | return GL_UNSIGNED_BYTE; |
| 36 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 33 | case Maxwell::VertexAttribute::Size::Size_16_16: | 37 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 38 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 34 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | 39 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: |
| 35 | return GL_UNSIGNED_SHORT; | 40 | return GL_UNSIGNED_SHORT; |
| 41 | case Maxwell::VertexAttribute::Size::Size_32: | ||
| 42 | case Maxwell::VertexAttribute::Size::Size_32_32: | ||
| 43 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | ||
| 44 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | ||
| 45 | return GL_UNSIGNED_INT; | ||
| 36 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 46 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 37 | return GL_UNSIGNED_INT_2_10_10_10_REV; | 47 | return GL_UNSIGNED_INT_2_10_10_10_REV; |
| 38 | } | 48 | } |
| @@ -42,16 +52,25 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 42 | return {}; | 52 | return {}; |
| 43 | } | 53 | } |
| 44 | 54 | ||
| 55 | case Maxwell::VertexAttribute::Type::SignedInt: | ||
| 45 | case Maxwell::VertexAttribute::Type::SignedNorm: { | 56 | case Maxwell::VertexAttribute::Type::SignedNorm: { |
| 46 | 57 | ||
| 47 | switch (attrib.size) { | 58 | switch (attrib.size) { |
| 48 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | 59 | case Maxwell::VertexAttribute::Size::Size_8: |
| 49 | return GL_INT; | ||
| 50 | case Maxwell::VertexAttribute::Size::Size_8_8: | 60 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 61 | case Maxwell::VertexAttribute::Size::Size_8_8_8: | ||
| 51 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 62 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 52 | return GL_BYTE; | 63 | return GL_BYTE; |
| 64 | case Maxwell::VertexAttribute::Size::Size_16: | ||
| 53 | case Maxwell::VertexAttribute::Size::Size_16_16: | 65 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| 66 | case Maxwell::VertexAttribute::Size::Size_16_16_16: | ||
| 67 | case Maxwell::VertexAttribute::Size::Size_16_16_16_16: | ||
| 54 | return GL_SHORT; | 68 | return GL_SHORT; |
| 69 | case Maxwell::VertexAttribute::Size::Size_32: | ||
| 70 | case Maxwell::VertexAttribute::Size::Size_32_32: | ||
| 71 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | ||
| 72 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | ||
| 73 | return GL_INT; | ||
| 55 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 74 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 56 | return GL_INT_2_10_10_10_REV; | 75 | return GL_INT_2_10_10_10_REV; |
| 57 | } | 76 | } |
| @@ -61,9 +80,6 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 61 | return {}; | 80 | return {}; |
| 62 | } | 81 | } |
| 63 | 82 | ||
| 64 | case Maxwell::VertexAttribute::Type::UnsignedInt: | ||
| 65 | return GL_UNSIGNED_INT; | ||
| 66 | |||
| 67 | case Maxwell::VertexAttribute::Type::Float: | 83 | case Maxwell::VertexAttribute::Type::Float: |
| 68 | return GL_FLOAT; | 84 | return GL_FLOAT; |
| 69 | } | 85 | } |
| @@ -91,6 +107,8 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { | |||
| 91 | switch (topology) { | 107 | switch (topology) { |
| 92 | case Maxwell::PrimitiveTopology::Points: | 108 | case Maxwell::PrimitiveTopology::Points: |
| 93 | return GL_POINTS; | 109 | return GL_POINTS; |
| 110 | case Maxwell::PrimitiveTopology::LineStrip: | ||
| 111 | return GL_LINE_STRIP; | ||
| 94 | case Maxwell::PrimitiveTopology::Triangles: | 112 | case Maxwell::PrimitiveTopology::Triangles: |
| 95 | return GL_TRIANGLES; | 113 | return GL_TRIANGLES; |
| 96 | case Maxwell::PrimitiveTopology::TriangleStrip: | 114 | case Maxwell::PrimitiveTopology::TriangleStrip: |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b5f97f332..f7eee7769 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -6,7 +6,10 @@ | |||
| 6 | #include <clocale> | 6 | #include <clocale> |
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | |||
| 10 | #include <fmt/ostream.h> | ||
| 9 | #include <glad/glad.h> | 11 | #include <glad/glad.h> |
| 12 | |||
| 10 | #define QT_NO_OPENGL | 13 | #define QT_NO_OPENGL |
| 11 | #include <QDesktopWidget> | 14 | #include <QDesktopWidget> |
| 12 | #include <QFileDialog> | 15 | #include <QFileDialog> |
| @@ -460,7 +463,7 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||
| 460 | "While attempting to load the ROM requested, an error occured. Please " | 463 | "While attempting to load the ROM requested, an error occured. Please " |
| 461 | "refer to the yuzu wiki for more information or the yuzu discord for " | 464 | "refer to the yuzu wiki for more information or the yuzu discord for " |
| 462 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", | 465 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", |
| 463 | loader_id, error_id, Loader::GetMessageForResultStatus(error_id)))); | 466 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)))); |
| 464 | } else { | 467 | } else { |
| 465 | QMessageBox::critical( | 468 | QMessageBox::critical( |
| 466 | this, tr("Error while loading ROM!"), | 469 | this, tr("Error while loading ROM!"), |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index e44a98311..9095cf27d 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -7,6 +7,8 @@ | |||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | 9 | ||
| 10 | #include <fmt/ostream.h> | ||
| 11 | |||
| 10 | #include "common/common_paths.h" | 12 | #include "common/common_paths.h" |
| 11 | #include "common/logging/backend.h" | 13 | #include "common/logging/backend.h" |
| 12 | #include "common/logging/filter.h" | 14 | #include "common/logging/filter.h" |
| @@ -194,7 +196,7 @@ int main(int argc, char** argv) { | |||
| 194 | "While attempting to load the ROM requested, an error occured. Please " | 196 | "While attempting to load the ROM requested, an error occured. Please " |
| 195 | "refer to the yuzu wiki for more information or the yuzu discord for " | 197 | "refer to the yuzu wiki for more information or the yuzu discord for " |
| 196 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", | 198 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", |
| 197 | loader_id, error_id, Loader::GetMessageForResultStatus(error_id)); | 199 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); |
| 198 | } | 200 | } |
| 199 | } | 201 | } |
| 200 | 202 | ||