summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt8
-rw-r--r--src/audio_core/algorithm/filter.cpp79
-rw-r--r--src/audio_core/algorithm/filter.h62
-rw-r--r--src/audio_core/algorithm/interpolate.cpp71
-rw-r--r--src/audio_core/algorithm/interpolate.h43
-rw-r--r--src/audio_core/audio_renderer.cpp17
-rw-r--r--src/audio_core/audio_renderer.h7
-rw-r--r--src/audio_core/cubeb_sink.cpp6
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/break_points.cpp90
-rw-r--r--src/common/break_points.h49
-rw-r--r--src/common/logging/backend.cpp5
-rw-r--r--src/common/misc.cpp2
-rw-r--r--src/common/thread_queue_list.h10
-rw-r--r--src/common/x64/xbyak_abi.h20
-rw-r--r--src/common/x64/xbyak_util.h6
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp15
-rw-r--r--src/core/core_cpu.cpp4
-rw-r--r--src/core/core_cpu.h2
-rw-r--r--src/core/core_timing.cpp12
-rw-r--r--src/core/core_timing.h1
-rw-r--r--src/core/file_sys/card_image.cpp15
-rw-r--r--src/core/file_sys/card_image.h6
-rw-r--r--src/core/file_sys/partition_filesystem.h2
-rw-r--r--src/core/file_sys/program_metadata.h2
-rw-r--r--src/core/file_sys/vfs.cpp20
-rw-r--r--src/core/file_sys/vfs.h20
-rw-r--r--src/core/file_sys/vfs_offset.h3
-rw-r--r--src/core/file_sys/vfs_vector.h3
-rw-r--r--src/core/frontend/emu_window.h6
-rw-r--r--src/core/hle/kernel/kernel.cpp2
-rw-r--r--src/core/hle/kernel/object.h15
-rw-r--r--src/core/hle/kernel/scheduler.cpp2
-rw-r--r--src/core/hle/kernel/scheduler.h2
-rw-r--r--src/core/hle/kernel/server_session.cpp2
-rw-r--r--src/core/hle/kernel/server_session.h7
-rw-r--r--src/core/hle/kernel/svc.cpp11
-rw-r--r--src/core/hle/kernel/thread.cpp34
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp44
-rw-r--r--src/core/hle/service/audio/audren_u.h1
-rw-r--r--src/core/hle/service/friend/friend.cpp9
-rw-r--r--src/core/hle/service/hid/hid.cpp17
-rw-r--r--src/core/hle/service/lm/lm.cpp22
-rw-r--r--src/core/hle/service/mm/mm_u.cpp83
-rw-r--r--src/core/hle/service/mm/mm_u.h15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp34
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h36
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp34
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h36
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp4
-rw-r--r--src/core/hle/service/service.h2
-rw-r--r--src/core/hle/service/sm/controller.cpp4
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp1
-rw-r--r--src/core/loader/elf.cpp1
-rw-r--r--src/core/loader/loader.cpp18
-rw-r--r--src/core/loader/loader.h13
-rw-r--r--src/core/loader/nro.cpp1
-rw-r--r--src/core/loader/nso.cpp1
-rw-r--r--src/video_core/engines/shader_bytecode.h29
-rw-r--r--src/video_core/gpu.cpp5
-rw-r--r--src/video_core/gpu.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp198
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h23
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp113
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h225
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp206
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_state.h6
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp201
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.h42
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h28
-rw-r--r--src/yuzu/main.cpp5
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
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 @@
1add_library(audio_core STATIC 1add_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
14namespace AudioCore {
15
16Filter 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
32Filter::Filter() : Filter(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) {}
33
34Filter::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
37void 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.
57static 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
62CascadingFilter 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
70CascadingFilter::CascadingFilter() = default;
71CascadingFilter::CascadingFilter(std::vector<Filter> filters) : filters(std::move(filters)) {}
72
73void 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
11namespace 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
18class Filter {
19public:
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
32private:
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.
44class CascadingFilter {
45public:
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
58private:
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
14namespace AudioCore {
15
16/// The Lanczos kernel
17static 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
24std::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
12namespace AudioCore {
13
14struct 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.
30std::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.
37inline 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
30u32 AudioRenderer::GetSampleRate() const {
31 return worker_params.sample_rate;
32}
33
34u32 AudioRenderer::GetSampleCount() const {
35 return worker_params.sample_count;
36}
37
38u32 AudioRenderer::GetMixBufferCount() const {
39 return worker_params.mix_buffer_count;
40}
41
29std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { 42std::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 {
26struct AudioRendererParameter { 27struct 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
164private: 168private:
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
9bool 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
15bool 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
23BreakPoints::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
36void 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
48void 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
56void 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
71void 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
78void 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
11class DebugInterface;
12
13struct TBreakPoint {
14 u32 iAddress;
15 bool bOn;
16 bool bTemporary;
17};
18
19// Code breakpoints.
20class BreakPoints {
21public:
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
46private:
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) {
302void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, 302void 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
12namespace Common { 12namespace Common::X64 {
13namespace X64 {
14 13
15int RegToIndex(const Xbyak::Reg& reg) { 14inline 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
155void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size, 154inline 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
177size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, 176inline 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
198void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, size_t rsp_alignment, 197inline 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
11namespace Common { 11namespace Common::X64 {
12namespace X64 {
13 12
14// Constants for use with cmpps/cmpss 13// Constants for use with cmpps/cmpss
15enum { 14enum {
@@ -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
236void ARM_Dynarmic::PrepareReschedule() { 245void ARM_Dynarmic::PrepareReschedule() {
237 if (jit->IsExecuting()) { 246 jit->HaltExecution();
238 jit->HaltExecution();
239 }
240} 247}
241 248
242void ARM_Dynarmic::ClearInstructionCache() { 249void 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
19namespace Core { 20namespace 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
57static Common::MPSCQueue<Event, false> ts_queue; 57static Common::MPSCQueue<Event, false> ts_queue;
58 58
59// the queue for unscheduling the events from other threads threadsafe
60static Common::MPSCQueue<std::pair<const EventType*, u64>, false> unschedule_queue;
61
59constexpr int MAX_SLICE_LENGTH = 20000; 62constexpr int MAX_SLICE_LENGTH = 20000;
60 63
61static s64 idled_cycles; 64static s64 idled_cycles;
@@ -135,11 +138,9 @@ void ClearPendingEvents() {
135void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { 138void 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
164void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
165 unschedule_queue.Push(std::make_pair(event_type, userdata));
166}
167
163void RemoveEvent(const EventType* event_type) { 168void 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
197void Advance() { 202void 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
65void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata); 65void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata);
66 66
67void UnscheduleEvent(const EventType* event_type, u64 userdata); 67void UnscheduleEvent(const EventType* event_type, u64 userdata);
68void 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.
70void RemoveEvent(const EventType* event_type); 71void 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
13namespace FileSys { 16namespace FileSys {
14 17
@@ -111,19 +114,19 @@ VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
111 return nullptr; 114 return nullptr;
112} 115}
113 116
114std::vector<std::shared_ptr<VfsFile>> XCI::GetFiles() const { 117std::vector<VirtualFile> XCI::GetFiles() const {
115 return {}; 118 return {};
116} 119}
117 120
118std::vector<std::shared_ptr<VfsDirectory>> XCI::GetSubdirectories() const { 121std::vector<VirtualDir> XCI::GetSubdirectories() const {
119 return std::vector<std::shared_ptr<VfsDirectory>>(); 122 return {};
120} 123}
121 124
122std::string XCI::GetName() const { 125std::string XCI::GetName() const {
123 return file->GetName(); 126 return file->GetName();
124} 127}
125 128
126std::shared_ptr<VfsDirectory> XCI::GetParentDirectory() const { 129VirtualDir 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
83protected: 83protected:
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
15namespace Loader { 15namespace Loader {
16enum class ResultStatus; 16enum class ResultStatus : u16;
17} 17}
18 18
19namespace FileSys { 19namespace 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
15namespace Loader { 15namespace Loader {
16enum class ResultStatus; 16enum class ResultStatus : u16;
17} 17}
18 18
19namespace FileSys { 19namespace 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
77VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { 77VirtualFile 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
140VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) { 140VirtualDir 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
16namespace FileSys { 16namespace FileSys {
17 17
18struct VfsFilesystem; 18class VfsDirectory;
19struct VfsFile; 19class VfsFile;
20struct VfsDirectory; 20class VfsFilesystem;
21 21
22// Convenience typedefs to use Vfs* interfaces 22// Convenience typedefs to use Vfs* interfaces
23using VirtualFilesystem = std::shared_ptr<VfsFilesystem>; 23using 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.
37struct VfsFilesystem : NonCopyable { 37class VfsFilesystem : NonCopyable {
38 VfsFilesystem(VirtualDir root); 38public:
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.
84struct VfsFile : NonCopyable { 85class VfsFile : NonCopyable {
86public:
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.
182struct VfsDirectory : NonCopyable { 184class VfsDirectory : NonCopyable {
185public:
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.
298struct ReadOnlyVfsDirectory : public VfsDirectory { 301class ReadOnlyVfsDirectory : public VfsDirectory {
302public:
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.
18struct OffsetVfsFile : public VfsFile { 18class OffsetVfsFile : public VfsFile {
19public:
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.
13struct VectorVfsDirectory : public VfsDirectory { 13class VectorVfsDirectory : public VfsDirectory {
14public:
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 {
34public: 34public:
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
12namespace Kernel { 12namespace Kernel {
13 13
14unsigned int Object::next_object_id; 14std::atomic<u32> Object::next_object_id{0};
15 15
16/// Initialize the kernel 16/// Initialize the kernel
17void Init() { 17void 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
63public: 64public:
64 static unsigned int next_object_id; 65 static std::atomic<u32> next_object_id;
65 66
66private: 67private:
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
75inline void intrusive_ptr_add_ref(Object* object) { 76inline 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
79inline void intrusive_ptr_release(Object* object) { 80inline 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
28bool Scheduler::HaveReadyThreads() { 28bool 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
253static void Break(u64 unk_0, u64 unk_1, u64 unk_2) { 253static 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 */
105static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { 106static 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
162void Thread::CancelWakeupTimer() { 169void Thread::CancelWakeupTimer() {
163 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); 170 CoreTiming::UnscheduleEventThreadsafe(ThreadWakeupEventType, callback_handle);
164} 171}
165 172
166static boost::optional<s32> GetNextProcessorId(u64 mask) { 173static boost::optional<s32> GetNextProcessorId(u64 mask) {
@@ -419,12 +426,33 @@ VAddr Thread::GetCommandBufferAddress() const {
419} 426}
420 427
421void Thread::AddMutexWaiter(SharedPtr<Thread> thread) { 428void 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
427void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) { 449void 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};
28class IAudioOut final : public ServiceFramework<IAudioOut> { 28class IAudioOut final : public ServiceFramework<IAudioOut> {
29public: 29public:
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
278void 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
256bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { 288bool 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
104void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { 111void 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:
291class Hid final : public ServiceFramework<Hid> { 291class Hid final : public ServiceFramework<Hid> {
292public: 292public:
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
10namespace Service::MM { 10namespace Service::MM {
11 11
12void InstallInterfaces(SM::ServiceManager& service_manager) { 12class MM_U final : public ServiceFramework<MM_U> {
13 std::make_shared<MM_U>()->InstallAsService(service_manager); 13public:
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
16void 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
22void MM_U::SetAndWait(Kernel::HLERequestContext& ctx) { 31private:
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
33void 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
40MM_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
67void 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
9namespace Service::MM { 9namespace Service::MM {
10 10
11class MM_U final : public ServiceFramework<MM_U> {
12public:
13 MM_U();
14 ~MM_U() = default;
15
16private:
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.
27void InstallInterfaces(SM::ServiceManager& service_manager); 12void 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
11namespace Service::Nvidia::Devices {
12
13u32 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
26u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
27 IoctlSetNvmapFD params{};
28 std::memcpy(&params, 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
12namespace Service::Nvidia::Devices {
13
14class nvhost_nvjpg final : public nvdevice {
15public:
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
21private:
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
11namespace Service::Nvidia::Devices {
12
13u32 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
26u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
27 IoctlSetNvmapFD params{};
28 std::memcpy(&params, 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
12namespace Service::Nvidia::Devices {
13
14class nvhost_vic final : public nvdevice {
15public:
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
21private:
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
44u32 Module::Open(const std::string& device_name) { 48u32 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
25namespace FileSys { 25namespace FileSys {
26struct VfsFilesystem; 26class VfsFilesystem;
27} 27}
28 28
29namespace Service { 29namespace 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 @@
10namespace Service::SM { 10namespace Service::SM {
11 11
12void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) { 12void 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) {
41void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { 41void 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
18namespace Loader { 19namespace Loader {
19 20
20const 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
26FileType IdentifyFile(FileSys::VirtualFile file) { 21FileType 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
130std::string GetMessageForResultStatus(ResultStatus status) { 125std::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
134std::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);
56std::string GetFileTypeString(FileType type); 56std::string GetFileTypeString(FileType type);
57 57
58/// Return type for functions in Loader namespace 58/// Return type for functions in Loader namespace
59enum class ResultStatus { 59enum 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
98std::string GetMessageForResultStatus(ResultStatus status); 98std::ostream& operator<<(std::ostream& os, ResultStatus status);
99std::string GetMessageForResultStatus(u16 status);
100 99
101/// Interface for loading an application 100/// Interface for loading an application
102class AppLoader : NonCopyable { 101class 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 */
214extern 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
203enum class XmadMode : u64 {
204 None = 0,
205 CLo = 1,
206 CHi = 2,
207 CSfu = 3,
208 CBcc = 4,
209};
210
203enum class FlowCondition : u64 { 211enum 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));
36MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); 36MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
37MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); 37MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
38 38
39RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) : emu_window{window} { 39RasterizerOpenGL::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
114RasterizerOpenGL::~RasterizerOpenGL() { 96RasterizerOpenGL::~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
122std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, 98std::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
204void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { 178std::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
304size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 269size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -432,6 +397,31 @@ void RasterizerOpenGL::Clear() {
432 } 397 }
433} 398}
434 399
400std::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
408std::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
435void RasterizerOpenGL::DrawArrays() { 425void 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
650u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program, 630std::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
722u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit, 690u32 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 {
94static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ 94static 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
234static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr), 240static 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
260static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr), 294static 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
424static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { 466static 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
12class OrphanBuffer : public OGLStreamBuffer { 12OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent)
13public: 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
17private:
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
27class StorageBuffer : public OGLStreamBuffer {
28public:
29 explicit StorageBuffer(GLenum target) : OGLStreamBuffer(target) {}
30 ~StorageBuffer() override;
31
32private:
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
49OGLStreamBuffer::OGLStreamBuffer(GLenum target) {
50 gl_target = target;
51}
52
53GLuint OGLStreamBuffer::GetHandle() const {
54 return gl_buffer.handle;
55}
56 16
57std::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
64OrphanBuffer::~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
68void OrphanBuffer::Create(size_t size, size_t /*sync_subdivide*/) { 40OGLStreamBuffer::~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
81void OrphanBuffer::Release() {
82 gl_buffer.Release(); 45 gl_buffer.Release();
83} 46}
84 47
85std::pair<u8*, GLintptr> OrphanBuffer::Map(size_t size, size_t alignment) { 48GLuint 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
96void 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
102StorageBuffer::~StorageBuffer() {
103 Release();
104} 50}
105 51
106void StorageBuffer::Create(size_t size, size_t sync_subdivide) { 52GLsizeiptr 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
124void StorageBuffer::Release() { 56std::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
135std::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
87void 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
178void 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
12class OGLStreamBuffer : private NonCopyable { 10class OGLStreamBuffer : private NonCopyable {
13public: 11public:
14 explicit OGLStreamBuffer(GLenum target); 12 explicit OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent = false);
15 virtual ~OGLStreamBuffer() = default; 13 ~OGLStreamBuffer();
16
17public:
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
28protected: 30private:
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
25inline GLenum VertexType(Maxwell::VertexAttribute attrib) { 25inline 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