diff options
72 files changed, 1917 insertions, 446 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 6f0ff953a..23e5d3f10 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -30,7 +30,7 @@ public: | |||
| 30 | return info; | 30 | return info; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | VoiceInfo& Info() { | 33 | VoiceInfo& GetInfo() { |
| 34 | return info; | 34 | return info; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| @@ -51,9 +51,30 @@ private: | |||
| 51 | VoiceInfo info{}; | 51 | VoiceInfo info{}; |
| 52 | }; | 52 | }; |
| 53 | 53 | ||
| 54 | class AudioRenderer::EffectState { | ||
| 55 | public: | ||
| 56 | const EffectOutStatus& GetOutStatus() const { | ||
| 57 | return out_status; | ||
| 58 | } | ||
| 59 | |||
| 60 | const EffectInStatus& GetInfo() const { | ||
| 61 | return info; | ||
| 62 | } | ||
| 63 | |||
| 64 | EffectInStatus& GetInfo() { | ||
| 65 | return info; | ||
| 66 | } | ||
| 67 | |||
| 68 | void UpdateState(); | ||
| 69 | |||
| 70 | private: | ||
| 71 | EffectOutStatus out_status{}; | ||
| 72 | EffectInStatus info{}; | ||
| 73 | }; | ||
| 54 | AudioRenderer::AudioRenderer(AudioRendererParameter params, | 74 | AudioRenderer::AudioRenderer(AudioRendererParameter params, |
| 55 | Kernel::SharedPtr<Kernel::Event> buffer_event) | 75 | Kernel::SharedPtr<Kernel::Event> buffer_event) |
| 56 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { | 76 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), |
| 77 | effects(params.effect_count) { | ||
| 57 | 78 | ||
| 58 | audio_out = std::make_unique<AudioCore::AudioOut>(); | 79 | audio_out = std::make_unique<AudioCore::AudioOut>(); |
| 59 | stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", | 80 | stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", |
| @@ -96,11 +117,29 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 96 | memory_pool_count * sizeof(MemoryPoolInfo)); | 117 | memory_pool_count * sizeof(MemoryPoolInfo)); |
| 97 | 118 | ||
| 98 | // Copy VoiceInfo structs | 119 | // Copy VoiceInfo structs |
| 99 | std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + | 120 | std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size + |
| 100 | config.voice_resource_size}; | 121 | config.memory_pools_size + config.voice_resource_size}; |
| 101 | for (auto& voice : voices) { | 122 | for (auto& voice : voices) { |
| 102 | std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); | 123 | std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo)); |
| 103 | offset += sizeof(VoiceInfo); | 124 | voice_offset += sizeof(VoiceInfo); |
| 125 | } | ||
| 126 | |||
| 127 | std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size + | ||
| 128 | config.memory_pools_size + config.voice_resource_size + | ||
| 129 | config.voices_size}; | ||
| 130 | for (auto& effect : effects) { | ||
| 131 | std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus)); | ||
| 132 | effect_offset += sizeof(EffectInStatus); | ||
| 133 | } | ||
| 134 | |||
| 135 | // Update memory pool state | ||
| 136 | std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); | ||
| 137 | for (std::size_t index = 0; index < memory_pool.size(); ++index) { | ||
| 138 | if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { | ||
| 139 | memory_pool[index].state = MemoryPoolStates::Attached; | ||
| 140 | } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { | ||
| 141 | memory_pool[index].state = MemoryPoolStates::Detached; | ||
| 142 | } | ||
| 104 | } | 143 | } |
| 105 | 144 | ||
| 106 | // Update voices | 145 | // Update voices |
| @@ -114,14 +153,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 114 | } | 153 | } |
| 115 | } | 154 | } |
| 116 | 155 | ||
| 117 | // Update memory pool state | 156 | for (auto& effect : effects) { |
| 118 | std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); | 157 | effect.UpdateState(); |
| 119 | for (std::size_t index = 0; index < memory_pool.size(); ++index) { | ||
| 120 | if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { | ||
| 121 | memory_pool[index].state = MemoryPoolStates::Attached; | ||
| 122 | } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { | ||
| 123 | memory_pool[index].state = MemoryPoolStates::Detached; | ||
| 124 | } | ||
| 125 | } | 158 | } |
| 126 | 159 | ||
| 127 | // Release previous buffers and queue next ones for playback | 160 | // Release previous buffers and queue next ones for playback |
| @@ -144,6 +177,14 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 144 | voice_out_status_offset += sizeof(VoiceOutStatus); | 177 | voice_out_status_offset += sizeof(VoiceOutStatus); |
| 145 | } | 178 | } |
| 146 | 179 | ||
| 180 | std::size_t effect_out_status_offset{ | ||
| 181 | sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + | ||
| 182 | response_data.voice_resource_size}; | ||
| 183 | for (const auto& effect : effects) { | ||
| 184 | std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(), | ||
| 185 | sizeof(EffectOutStatus)); | ||
| 186 | effect_out_status_offset += sizeof(EffectOutStatus); | ||
| 187 | } | ||
| 147 | return output_params; | 188 | return output_params; |
| 148 | } | 189 | } |
| 149 | 190 | ||
| @@ -244,11 +285,29 @@ void AudioRenderer::VoiceState::RefreshBuffer() { | |||
| 244 | break; | 285 | break; |
| 245 | } | 286 | } |
| 246 | 287 | ||
| 247 | samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE); | 288 | samples = |
| 289 | Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE); | ||
| 248 | 290 | ||
| 249 | is_refresh_pending = false; | 291 | is_refresh_pending = false; |
| 250 | } | 292 | } |
| 251 | 293 | ||
| 294 | void AudioRenderer::EffectState::UpdateState() { | ||
| 295 | if (info.is_new) { | ||
| 296 | out_status.state = EffectStatus::New; | ||
| 297 | } else { | ||
| 298 | if (info.type == Effect::Aux) { | ||
| 299 | ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0, | ||
| 300 | "Aux buffers tried to update"); | ||
| 301 | ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0, | ||
| 302 | "Aux buffers tried to update"); | ||
| 303 | ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0, | ||
| 304 | "Aux buffers tried to update"); | ||
| 305 | ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0, | ||
| 306 | "Aux buffers tried to update"); | ||
| 307 | } | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 252 | static constexpr s16 ClampToS16(s32 value) { | 311 | static constexpr s16 ClampToS16(s32 value) { |
| 253 | return static_cast<s16>(std::clamp(value, -32768, 32767)); | 312 | return static_cast<s16>(std::clamp(value, -32768, 32767)); |
| 254 | } | 313 | } |
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index dfef89e1d..046417da3 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -28,6 +28,16 @@ enum class PlayState : u8 { | |||
| 28 | Paused = 2, | 28 | Paused = 2, |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | enum class Effect : u8 { | ||
| 32 | None = 0, | ||
| 33 | Aux = 2, | ||
| 34 | }; | ||
| 35 | |||
| 36 | enum class EffectStatus : u8 { | ||
| 37 | None = 0, | ||
| 38 | New = 1, | ||
| 39 | }; | ||
| 40 | |||
| 31 | struct AudioRendererParameter { | 41 | struct AudioRendererParameter { |
| 32 | u32_le sample_rate; | 42 | u32_le sample_rate; |
| 33 | u32_le sample_count; | 43 | u32_le sample_count; |
| @@ -128,6 +138,43 @@ struct VoiceOutStatus { | |||
| 128 | }; | 138 | }; |
| 129 | static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); | 139 | static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); |
| 130 | 140 | ||
| 141 | struct AuxInfo { | ||
| 142 | std::array<u8, 24> input_mix_buffers; | ||
| 143 | std::array<u8, 24> output_mix_buffers; | ||
| 144 | u32_le mix_buffer_count; | ||
| 145 | u32_le sample_rate; // Stored in the aux buffer currently | ||
| 146 | u32_le sampe_count; | ||
| 147 | u64_le send_buffer_info; | ||
| 148 | u64_le send_buffer_base; | ||
| 149 | |||
| 150 | u64_le return_buffer_info; | ||
| 151 | u64_le return_buffer_base; | ||
| 152 | }; | ||
| 153 | static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size"); | ||
| 154 | |||
| 155 | struct EffectInStatus { | ||
| 156 | Effect type; | ||
| 157 | u8 is_new; | ||
| 158 | u8 is_enabled; | ||
| 159 | INSERT_PADDING_BYTES(1); | ||
| 160 | u32_le mix_id; | ||
| 161 | u64_le buffer_base; | ||
| 162 | u64_le buffer_sz; | ||
| 163 | s32_le priority; | ||
| 164 | INSERT_PADDING_BYTES(4); | ||
| 165 | union { | ||
| 166 | std::array<u8, 0xa0> raw; | ||
| 167 | AuxInfo aux_info; | ||
| 168 | }; | ||
| 169 | }; | ||
| 170 | static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size"); | ||
| 171 | |||
| 172 | struct EffectOutStatus { | ||
| 173 | EffectStatus state; | ||
| 174 | INSERT_PADDING_BYTES(0xf); | ||
| 175 | }; | ||
| 176 | static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size"); | ||
| 177 | |||
| 131 | struct UpdateDataHeader { | 178 | struct UpdateDataHeader { |
| 132 | UpdateDataHeader() {} | 179 | UpdateDataHeader() {} |
| 133 | 180 | ||
| @@ -173,11 +220,13 @@ public: | |||
| 173 | Stream::State GetStreamState() const; | 220 | Stream::State GetStreamState() const; |
| 174 | 221 | ||
| 175 | private: | 222 | private: |
| 223 | class EffectState; | ||
| 176 | class VoiceState; | 224 | class VoiceState; |
| 177 | 225 | ||
| 178 | AudioRendererParameter worker_params; | 226 | AudioRendererParameter worker_params; |
| 179 | Kernel::SharedPtr<Kernel::Event> buffer_event; | 227 | Kernel::SharedPtr<Kernel::Event> buffer_event; |
| 180 | std::vector<VoiceState> voices; | 228 | std::vector<VoiceState> voices; |
| 229 | std::vector<EffectState> effects; | ||
| 181 | std::unique_ptr<AudioOut> audio_out; | 230 | std::unique_ptr<AudioOut> audio_out; |
| 182 | AudioCore::StreamPtr stream; | 231 | AudioCore::StreamPtr stream; |
| 183 | }; | 232 | }; |
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp index 589ae5cbf..5b63f9e81 100644 --- a/src/common/hex_util.cpp +++ b/src/common/hex_util.cpp | |||
| @@ -18,6 +18,25 @@ u8 ToHexNibble(char c1) { | |||
| 18 | return 0; | 18 | return 0; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) { | ||
| 22 | std::vector<u8> out(str.size() / 2); | ||
| 23 | if (little_endian) { | ||
| 24 | for (std::size_t i = str.size() - 2; i <= str.size(); i -= 2) | ||
| 25 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); | ||
| 26 | } else { | ||
| 27 | for (std::size_t i = 0; i < str.size(); i += 2) | ||
| 28 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); | ||
| 29 | } | ||
| 30 | return out; | ||
| 31 | } | ||
| 32 | |||
| 33 | std::string HexVectorToString(const std::vector<u8>& vector, bool upper) { | ||
| 34 | std::string out; | ||
| 35 | for (u8 c : vector) | ||
| 36 | out += fmt::format(upper ? "{:02X}" : "{:02x}", c); | ||
| 37 | return out; | ||
| 38 | } | ||
| 39 | |||
| 21 | std::array<u8, 16> operator""_array16(const char* str, std::size_t len) { | 40 | std::array<u8, 16> operator""_array16(const char* str, std::size_t len) { |
| 22 | if (len != 32) { | 41 | if (len != 32) { |
| 23 | LOG_ERROR(Common, | 42 | LOG_ERROR(Common, |
diff --git a/src/common/hex_util.h b/src/common/hex_util.h index 863a5ccd9..68f003cb6 100644 --- a/src/common/hex_util.h +++ b/src/common/hex_util.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include <vector> | ||
| 10 | #include <fmt/format.h> | 11 | #include <fmt/format.h> |
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 12 | 13 | ||
| @@ -14,6 +15,8 @@ namespace Common { | |||
| 14 | 15 | ||
| 15 | u8 ToHexNibble(char c1); | 16 | u8 ToHexNibble(char c1); |
| 16 | 17 | ||
| 18 | std::vector<u8> HexStringToVector(std::string_view str, bool little_endian); | ||
| 19 | |||
| 17 | template <std::size_t Size, bool le = false> | 20 | template <std::size_t Size, bool le = false> |
| 18 | std::array<u8, Size> HexStringToArray(std::string_view str) { | 21 | std::array<u8, Size> HexStringToArray(std::string_view str) { |
| 19 | std::array<u8, Size> out{}; | 22 | std::array<u8, Size> out{}; |
| @@ -27,6 +30,8 @@ std::array<u8, Size> HexStringToArray(std::string_view str) { | |||
| 27 | return out; | 30 | return out; |
| 28 | } | 31 | } |
| 29 | 32 | ||
| 33 | std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true); | ||
| 34 | |||
| 30 | template <std::size_t Size> | 35 | template <std::size_t Size> |
| 31 | std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { | 36 | std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { |
| 32 | std::string out; | 37 | std::string out; |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 7e978cf7a..0762321a9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -129,7 +129,7 @@ public: | |||
| 129 | }; | 129 | }; |
| 130 | 130 | ||
| 131 | std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { | 131 | std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { |
| 132 | auto& current_process = Core::CurrentProcess(); | 132 | auto* current_process = Core::CurrentProcess(); |
| 133 | auto** const page_table = current_process->VMManager().page_table.pointers.data(); | 133 | auto** const page_table = current_process->VMManager().page_table.pointers.data(); |
| 134 | 134 | ||
| 135 | Dynarmic::A64::UserConfig config; | 135 | Dynarmic::A64::UserConfig config; |
diff --git a/src/core/core.cpp b/src/core/core.cpp index b6acfb3e4..e2fb9e038 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -136,7 +136,8 @@ struct System::Impl { | |||
| 136 | if (virtual_filesystem == nullptr) | 136 | if (virtual_filesystem == nullptr) |
| 137 | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); | 137 | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); |
| 138 | 138 | ||
| 139 | kernel.MakeCurrentProcess(Kernel::Process::Create(kernel, "main")); | 139 | auto main_process = Kernel::Process::Create(kernel, "main"); |
| 140 | kernel.MakeCurrentProcess(main_process.get()); | ||
| 140 | 141 | ||
| 141 | cpu_barrier = std::make_shared<CpuBarrier>(); | 142 | cpu_barrier = std::make_shared<CpuBarrier>(); |
| 142 | cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); | 143 | cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); |
| @@ -361,11 +362,11 @@ const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_ind | |||
| 361 | return impl->cpu_cores[core_index]->Scheduler(); | 362 | return impl->cpu_cores[core_index]->Scheduler(); |
| 362 | } | 363 | } |
| 363 | 364 | ||
| 364 | Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() { | 365 | Kernel::Process* System::CurrentProcess() { |
| 365 | return impl->kernel.CurrentProcess(); | 366 | return impl->kernel.CurrentProcess(); |
| 366 | } | 367 | } |
| 367 | 368 | ||
| 368 | const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const { | 369 | const Kernel::Process* System::CurrentProcess() const { |
| 369 | return impl->kernel.CurrentProcess(); | 370 | return impl->kernel.CurrentProcess(); |
| 370 | } | 371 | } |
| 371 | 372 | ||
diff --git a/src/core/core.h b/src/core/core.h index f9a3e97e3..ea4d53914 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -174,11 +174,11 @@ public: | |||
| 174 | /// Gets the scheduler for the CPU core with the specified index | 174 | /// Gets the scheduler for the CPU core with the specified index |
| 175 | const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index); | 175 | const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index); |
| 176 | 176 | ||
| 177 | /// Provides a reference to the current process | 177 | /// Provides a pointer to the current process |
| 178 | Kernel::SharedPtr<Kernel::Process>& CurrentProcess(); | 178 | Kernel::Process* CurrentProcess(); |
| 179 | 179 | ||
| 180 | /// Provides a constant reference to the current process. | 180 | /// Provides a constant pointer to the current process. |
| 181 | const Kernel::SharedPtr<Kernel::Process>& CurrentProcess() const; | 181 | const Kernel::Process* CurrentProcess() const; |
| 182 | 182 | ||
| 183 | /// Provides a reference to the kernel instance. | 183 | /// Provides a reference to the kernel instance. |
| 184 | Kernel::KernelCore& Kernel(); | 184 | Kernel::KernelCore& Kernel(); |
| @@ -246,7 +246,7 @@ inline TelemetrySession& Telemetry() { | |||
| 246 | return System::GetInstance().TelemetrySession(); | 246 | return System::GetInstance().TelemetrySession(); |
| 247 | } | 247 | } |
| 248 | 248 | ||
| 249 | inline Kernel::SharedPtr<Kernel::Process>& CurrentProcess() { | 249 | inline Kernel::Process* CurrentProcess() { |
| 250 | return System::GetInstance().CurrentProcess(); | 250 | return System::GetInstance().CurrentProcess(); |
| 251 | } | 251 | } |
| 252 | 252 | ||
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index 2a913ce82..47b7526c7 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #include "common/alignment.h" | 26 | #include "common/alignment.h" |
| 27 | #include "common/assert.h" | 27 | #include "common/assert.h" |
| 28 | #include "core/file_sys/fsmitm_romfsbuild.h" | 28 | #include "core/file_sys/fsmitm_romfsbuild.h" |
| 29 | #include "core/file_sys/ips_layer.h" | ||
| 29 | #include "core/file_sys/vfs.h" | 30 | #include "core/file_sys/vfs.h" |
| 30 | #include "core/file_sys/vfs_vector.h" | 31 | #include "core/file_sys/vfs_vector.h" |
| 31 | 32 | ||
| @@ -123,7 +124,7 @@ static u64 romfs_get_hash_table_count(u64 num_entries) { | |||
| 123 | return count; | 124 | return count; |
| 124 | } | 125 | } |
| 125 | 126 | ||
| 126 | void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, | 127 | void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext, |
| 127 | std::shared_ptr<RomFSBuildDirectoryContext> parent) { | 128 | std::shared_ptr<RomFSBuildDirectoryContext> parent) { |
| 128 | std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; | 129 | std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; |
| 129 | 130 | ||
| @@ -144,6 +145,9 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, | |||
| 144 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); | 145 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); |
| 145 | child->path = parent->path + "/" + kv.first; | 146 | child->path = parent->path + "/" + kv.first; |
| 146 | 147 | ||
| 148 | if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr) | ||
| 149 | continue; | ||
| 150 | |||
| 147 | // Sanity check on path_len | 151 | // Sanity check on path_len |
| 148 | ASSERT(child->path_len < FS_MAX_PATH); | 152 | ASSERT(child->path_len < FS_MAX_PATH); |
| 149 | 153 | ||
| @@ -157,11 +161,24 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, | |||
| 157 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); | 161 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); |
| 158 | child->path = parent->path + "/" + kv.first; | 162 | child->path = parent->path + "/" + kv.first; |
| 159 | 163 | ||
| 164 | if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr) | ||
| 165 | continue; | ||
| 166 | |||
| 160 | // Sanity check on path_len | 167 | // Sanity check on path_len |
| 161 | ASSERT(child->path_len < FS_MAX_PATH); | 168 | ASSERT(child->path_len < FS_MAX_PATH); |
| 162 | 169 | ||
| 163 | child->source = root_romfs->GetFileRelative(child->path); | 170 | child->source = root_romfs->GetFileRelative(child->path); |
| 164 | 171 | ||
| 172 | if (ext != nullptr) { | ||
| 173 | const auto ips = ext->GetFileRelative(child->path + ".ips"); | ||
| 174 | |||
| 175 | if (ips != nullptr) { | ||
| 176 | auto patched = PatchIPS(child->source, ips); | ||
| 177 | if (patched != nullptr) | ||
| 178 | child->source = std::move(patched); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 165 | child->size = child->source->GetSize(); | 182 | child->size = child->source->GetSize(); |
| 166 | 183 | ||
| 167 | AddFile(parent, child); | 184 | AddFile(parent, child); |
| @@ -169,7 +186,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, | |||
| 169 | } | 186 | } |
| 170 | 187 | ||
| 171 | for (auto& child : child_dirs) { | 188 | for (auto& child : child_dirs) { |
| 172 | this->VisitDirectory(root_romfs, child); | 189 | this->VisitDirectory(root_romfs, ext, child); |
| 173 | } | 190 | } |
| 174 | } | 191 | } |
| 175 | 192 | ||
| @@ -208,14 +225,15 @@ bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> pare | |||
| 208 | return true; | 225 | return true; |
| 209 | } | 226 | } |
| 210 | 227 | ||
| 211 | RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) { | 228 | RomFSBuildContext::RomFSBuildContext(VirtualDir base_, VirtualDir ext_) |
| 229 | : base(std::move(base_)), ext(std::move(ext_)) { | ||
| 212 | root = std::make_shared<RomFSBuildDirectoryContext>(); | 230 | root = std::make_shared<RomFSBuildDirectoryContext>(); |
| 213 | root->path = "\0"; | 231 | root->path = "\0"; |
| 214 | directories.emplace(root->path, root); | 232 | directories.emplace(root->path, root); |
| 215 | num_dirs = 1; | 233 | num_dirs = 1; |
| 216 | dir_table_size = 0x18; | 234 | dir_table_size = 0x18; |
| 217 | 235 | ||
| 218 | VisitDirectory(base, root); | 236 | VisitDirectory(base, ext, root); |
| 219 | } | 237 | } |
| 220 | 238 | ||
| 221 | RomFSBuildContext::~RomFSBuildContext() = default; | 239 | RomFSBuildContext::~RomFSBuildContext() = default; |
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h index b0c3c123b..3d377b0af 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.h +++ b/src/core/file_sys/fsmitm_romfsbuild.h | |||
| @@ -40,7 +40,7 @@ struct RomFSFileEntry; | |||
| 40 | 40 | ||
| 41 | class RomFSBuildContext { | 41 | class RomFSBuildContext { |
| 42 | public: | 42 | public: |
| 43 | explicit RomFSBuildContext(VirtualDir base); | 43 | explicit RomFSBuildContext(VirtualDir base, VirtualDir ext = nullptr); |
| 44 | ~RomFSBuildContext(); | 44 | ~RomFSBuildContext(); |
| 45 | 45 | ||
| 46 | // This finalizes the context. | 46 | // This finalizes the context. |
| @@ -48,6 +48,7 @@ public: | |||
| 48 | 48 | ||
| 49 | private: | 49 | private: |
| 50 | VirtualDir base; | 50 | VirtualDir base; |
| 51 | VirtualDir ext; | ||
| 51 | std::shared_ptr<RomFSBuildDirectoryContext> root; | 52 | std::shared_ptr<RomFSBuildDirectoryContext> root; |
| 52 | std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories; | 53 | std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories; |
| 53 | std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files; | 54 | std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files; |
| @@ -59,7 +60,8 @@ private: | |||
| 59 | u64 file_hash_table_size = 0; | 60 | u64 file_hash_table_size = 0; |
| 60 | u64 file_partition_size = 0; | 61 | u64 file_partition_size = 0; |
| 61 | 62 | ||
| 62 | void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent); | 63 | void VisitDirectory(VirtualDir filesys, VirtualDir ext, |
| 64 | std::shared_ptr<RomFSBuildDirectoryContext> parent); | ||
| 63 | 65 | ||
| 64 | bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, | 66 | bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, |
| 65 | std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx); | 67 | std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx); |
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index df933ee36..554eae9bc 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp | |||
| @@ -2,7 +2,15 @@ | |||
| 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 "common/assert.h" | 5 | #include <algorithm> |
| 6 | #include <cstring> | ||
| 7 | #include <map> | ||
| 8 | #include <sstream> | ||
| 9 | #include <string> | ||
| 10 | #include <utility> | ||
| 11 | |||
| 12 | #include "common/hex_util.h" | ||
| 13 | #include "common/logging/log.h" | ||
| 6 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 7 | #include "core/file_sys/ips_layer.h" | 15 | #include "core/file_sys/ips_layer.h" |
| 8 | #include "core/file_sys/vfs_vector.h" | 16 | #include "core/file_sys/vfs_vector.h" |
| @@ -15,16 +23,48 @@ enum class IPSFileType { | |||
| 15 | Error, | 23 | Error, |
| 16 | }; | 24 | }; |
| 17 | 25 | ||
| 26 | constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{ | ||
| 27 | {"\\a", "\a"}, | ||
| 28 | {"\\b", "\b"}, | ||
| 29 | {"\\f", "\f"}, | ||
| 30 | {"\\n", "\n"}, | ||
| 31 | {"\\r", "\r"}, | ||
| 32 | {"\\t", "\t"}, | ||
| 33 | {"\\v", "\v"}, | ||
| 34 | {"\\\\", "\\"}, | ||
| 35 | {"\\\'", "\'"}, | ||
| 36 | {"\\\"", "\""}, | ||
| 37 | {"\\\?", "\?"}, | ||
| 38 | }}; | ||
| 39 | |||
| 18 | static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { | 40 | static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { |
| 19 | if (magic.size() != 5) | 41 | if (magic.size() != 5) { |
| 20 | return IPSFileType::Error; | 42 | return IPSFileType::Error; |
| 21 | if (magic == std::vector<u8>{'P', 'A', 'T', 'C', 'H'}) | 43 | } |
| 44 | |||
| 45 | constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}}; | ||
| 46 | if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) { | ||
| 22 | return IPSFileType::IPS; | 47 | return IPSFileType::IPS; |
| 23 | if (magic == std::vector<u8>{'I', 'P', 'S', '3', '2'}) | 48 | } |
| 49 | |||
| 50 | constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}}; | ||
| 51 | if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) { | ||
| 24 | return IPSFileType::IPS32; | 52 | return IPSFileType::IPS32; |
| 53 | } | ||
| 54 | |||
| 25 | return IPSFileType::Error; | 55 | return IPSFileType::Error; |
| 26 | } | 56 | } |
| 27 | 57 | ||
| 58 | static bool IsEOF(IPSFileType type, const std::vector<u8>& data) { | ||
| 59 | constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}}; | ||
| 60 | if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) { | ||
| 61 | return true; | ||
| 62 | } | ||
| 63 | |||
| 64 | constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}}; | ||
| 65 | return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin()); | ||
| 66 | } | ||
| 67 | |||
| 28 | VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | 68 | VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { |
| 29 | if (in == nullptr || ips == nullptr) | 69 | if (in == nullptr || ips == nullptr) |
| 30 | return nullptr; | 70 | return nullptr; |
| @@ -39,8 +79,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | |||
| 39 | u64 offset = 5; // After header | 79 | u64 offset = 5; // After header |
| 40 | while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { | 80 | while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { |
| 41 | offset += temp.size(); | 81 | offset += temp.size(); |
| 42 | if (type == IPSFileType::IPS32 && temp == std::vector<u8>{'E', 'E', 'O', 'F'} || | 82 | if (IsEOF(type, temp)) { |
| 43 | type == IPSFileType::IPS && temp == std::vector<u8>{'E', 'O', 'F'}) { | ||
| 44 | break; | 83 | break; |
| 45 | } | 84 | } |
| 46 | 85 | ||
| @@ -68,21 +107,232 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | |||
| 68 | return nullptr; | 107 | return nullptr; |
| 69 | 108 | ||
| 70 | if (real_offset + rle_size > in_data.size()) | 109 | if (real_offset + rle_size > in_data.size()) |
| 71 | rle_size = in_data.size() - real_offset; | 110 | rle_size = static_cast<u16>(in_data.size() - real_offset); |
| 72 | std::memset(in_data.data() + real_offset, data.get(), rle_size); | 111 | std::memset(in_data.data() + real_offset, data.get(), rle_size); |
| 73 | } else { // Standard Patch | 112 | } else { // Standard Patch |
| 74 | auto read = data_size; | 113 | auto read = data_size; |
| 75 | if (real_offset + read > in_data.size()) | 114 | if (real_offset + read > in_data.size()) |
| 76 | read = in_data.size() - real_offset; | 115 | read = static_cast<u16>(in_data.size() - real_offset); |
| 77 | if (ips->Read(in_data.data() + real_offset, read, offset) != data_size) | 116 | if (ips->Read(in_data.data() + real_offset, read, offset) != data_size) |
| 78 | return nullptr; | 117 | return nullptr; |
| 79 | offset += data_size; | 118 | offset += data_size; |
| 80 | } | 119 | } |
| 81 | } | 120 | } |
| 82 | 121 | ||
| 83 | if (temp != std::vector<u8>{'E', 'E', 'O', 'F'} && temp != std::vector<u8>{'E', 'O', 'F'}) | 122 | if (!IsEOF(type, temp)) { |
| 84 | return nullptr; | 123 | return nullptr; |
| 85 | return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); | 124 | } |
| 125 | |||
| 126 | return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), | ||
| 127 | in->GetContainingDirectory()); | ||
| 128 | } | ||
| 129 | |||
| 130 | struct IPSwitchCompiler::IPSwitchPatch { | ||
| 131 | std::string name; | ||
| 132 | bool enabled; | ||
| 133 | std::map<u32, std::vector<u8>> records; | ||
| 134 | }; | ||
| 135 | |||
| 136 | IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) { | ||
| 137 | Parse(); | ||
| 138 | } | ||
| 139 | |||
| 140 | IPSwitchCompiler::~IPSwitchCompiler() = default; | ||
| 141 | |||
| 142 | std::array<u8, 32> IPSwitchCompiler::GetBuildID() const { | ||
| 143 | return nso_build_id; | ||
| 144 | } | ||
| 145 | |||
| 146 | bool IPSwitchCompiler::IsValid() const { | ||
| 147 | return valid; | ||
| 148 | } | ||
| 149 | |||
| 150 | static bool StartsWith(std::string_view base, std::string_view check) { | ||
| 151 | return base.size() >= check.size() && base.substr(0, check.size()) == check; | ||
| 152 | } | ||
| 153 | |||
| 154 | static std::string EscapeStringSequences(std::string in) { | ||
| 155 | for (const auto& seq : ESCAPE_CHARACTER_MAP) { | ||
| 156 | for (auto index = in.find(seq.first); index != std::string::npos; | ||
| 157 | index = in.find(seq.first, index)) { | ||
| 158 | in.replace(index, std::strlen(seq.first), seq.second); | ||
| 159 | index += std::strlen(seq.second); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | return in; | ||
| 164 | } | ||
| 165 | |||
| 166 | void IPSwitchCompiler::ParseFlag(const std::string& line) { | ||
| 167 | if (StartsWith(line, "@flag offset_shift ")) { | ||
| 168 | // Offset Shift Flag | ||
| 169 | offset_shift = std::stoll(line.substr(19), nullptr, 0); | ||
| 170 | } else if (StartsWith(line, "@little-endian")) { | ||
| 171 | // Set values to read as little endian | ||
| 172 | is_little_endian = true; | ||
| 173 | } else if (StartsWith(line, "@big-endian")) { | ||
| 174 | // Set values to read as big endian | ||
| 175 | is_little_endian = false; | ||
| 176 | } else if (StartsWith(line, "@flag print_values")) { | ||
| 177 | // Force printing of applied values | ||
| 178 | print_values = true; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | void IPSwitchCompiler::Parse() { | ||
| 183 | const auto bytes = patch_text->ReadAllBytes(); | ||
| 184 | std::stringstream s; | ||
| 185 | s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size()); | ||
| 186 | |||
| 187 | std::vector<std::string> lines; | ||
| 188 | std::string stream_line; | ||
| 189 | while (std::getline(s, stream_line)) { | ||
| 190 | // Remove a trailing \r | ||
| 191 | if (!stream_line.empty() && stream_line.back() == '\r') | ||
| 192 | stream_line.pop_back(); | ||
| 193 | lines.push_back(std::move(stream_line)); | ||
| 194 | } | ||
| 195 | |||
| 196 | for (std::size_t i = 0; i < lines.size(); ++i) { | ||
| 197 | auto line = lines[i]; | ||
| 198 | |||
| 199 | // Remove midline comments | ||
| 200 | std::size_t comment_index = std::string::npos; | ||
| 201 | bool within_string = false; | ||
| 202 | for (std::size_t k = 0; k < line.size(); ++k) { | ||
| 203 | if (line[k] == '\"' && (k > 0 && line[k - 1] != '\\')) { | ||
| 204 | within_string = !within_string; | ||
| 205 | } else if (line[k] == '\\' && (k < line.size() - 1 && line[k + 1] == '\\')) { | ||
| 206 | comment_index = k; | ||
| 207 | break; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | if (!StartsWith(line, "//") && comment_index != std::string::npos) { | ||
| 212 | last_comment = line.substr(comment_index + 2); | ||
| 213 | line = line.substr(0, comment_index); | ||
| 214 | } | ||
| 215 | |||
| 216 | if (StartsWith(line, "@stop")) { | ||
| 217 | // Force stop | ||
| 218 | break; | ||
| 219 | } else if (StartsWith(line, "@nsobid-")) { | ||
| 220 | // NSO Build ID Specifier | ||
| 221 | auto raw_build_id = line.substr(8); | ||
| 222 | if (raw_build_id.size() != 0x40) | ||
| 223 | raw_build_id.resize(0x40, '0'); | ||
| 224 | nso_build_id = Common::HexStringToArray<0x20>(raw_build_id); | ||
| 225 | } else if (StartsWith(line, "#")) { | ||
| 226 | // Mandatory Comment | ||
| 227 | LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}", | ||
| 228 | patch_text->GetName(), line.substr(1)); | ||
| 229 | } else if (StartsWith(line, "//")) { | ||
| 230 | // Normal Comment | ||
| 231 | last_comment = line.substr(2); | ||
| 232 | if (last_comment.find_first_not_of(' ') == std::string::npos) | ||
| 233 | continue; | ||
| 234 | if (last_comment.find_first_not_of(' ') != 0) | ||
| 235 | last_comment = last_comment.substr(last_comment.find_first_not_of(' ')); | ||
| 236 | } else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) { | ||
| 237 | // Start of patch | ||
| 238 | const auto enabled = StartsWith(line, "@enabled"); | ||
| 239 | if (i == 0) | ||
| 240 | return; | ||
| 241 | LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Parsing patch '{}' ({})", | ||
| 242 | patch_text->GetName(), last_comment, line.substr(1)); | ||
| 243 | |||
| 244 | IPSwitchPatch patch{last_comment, enabled, {}}; | ||
| 245 | |||
| 246 | // Read rest of patch | ||
| 247 | while (true) { | ||
| 248 | if (i + 1 >= lines.size()) | ||
| 249 | break; | ||
| 250 | const auto patch_line = lines[++i]; | ||
| 251 | |||
| 252 | // Start of new patch | ||
| 253 | if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) { | ||
| 254 | --i; | ||
| 255 | break; | ||
| 256 | } | ||
| 257 | |||
| 258 | // Check for a flag | ||
| 259 | if (StartsWith(patch_line, "@")) { | ||
| 260 | ParseFlag(patch_line); | ||
| 261 | continue; | ||
| 262 | } | ||
| 263 | |||
| 264 | // 11 - 8 hex digit offset + space + minimum two digit overwrite val | ||
| 265 | if (patch_line.length() < 11) | ||
| 266 | break; | ||
| 267 | auto offset = std::stoul(patch_line.substr(0, 8), nullptr, 16); | ||
| 268 | offset += static_cast<unsigned long>(offset_shift); | ||
| 269 | |||
| 270 | std::vector<u8> replace; | ||
| 271 | // 9 - first char of replacement val | ||
| 272 | if (patch_line[9] == '\"') { | ||
| 273 | // string replacement | ||
| 274 | auto end_index = patch_line.find('\"', 10); | ||
| 275 | if (end_index == std::string::npos || end_index < 10) | ||
| 276 | return; | ||
| 277 | while (patch_line[end_index - 1] == '\\') { | ||
| 278 | end_index = patch_line.find('\"', end_index + 1); | ||
| 279 | if (end_index == std::string::npos || end_index < 10) | ||
| 280 | return; | ||
| 281 | } | ||
| 282 | |||
| 283 | auto value = patch_line.substr(10, end_index - 10); | ||
| 284 | value = EscapeStringSequences(value); | ||
| 285 | replace.reserve(value.size()); | ||
| 286 | std::copy(value.begin(), value.end(), std::back_inserter(replace)); | ||
| 287 | } else { | ||
| 288 | // hex replacement | ||
| 289 | const auto value = patch_line.substr(9); | ||
| 290 | replace.reserve(value.size() / 2); | ||
| 291 | replace = Common::HexStringToVector(value, is_little_endian); | ||
| 292 | } | ||
| 293 | |||
| 294 | if (print_values) { | ||
| 295 | LOG_INFO(Loader, | ||
| 296 | "[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} " | ||
| 297 | "with byte string '{}'", | ||
| 298 | patch_text->GetName(), offset, Common::HexVectorToString(replace)); | ||
| 299 | } | ||
| 300 | |||
| 301 | patch.records.insert_or_assign(offset, std::move(replace)); | ||
| 302 | } | ||
| 303 | |||
| 304 | patches.push_back(std::move(patch)); | ||
| 305 | } else if (StartsWith(line, "@")) { | ||
| 306 | ParseFlag(line); | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | valid = true; | ||
| 311 | } | ||
| 312 | |||
| 313 | VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const { | ||
| 314 | if (in == nullptr || !valid) | ||
| 315 | return nullptr; | ||
| 316 | |||
| 317 | auto in_data = in->ReadAllBytes(); | ||
| 318 | |||
| 319 | for (const auto& patch : patches) { | ||
| 320 | if (!patch.enabled) | ||
| 321 | continue; | ||
| 322 | |||
| 323 | for (const auto& record : patch.records) { | ||
| 324 | if (record.first >= in_data.size()) | ||
| 325 | continue; | ||
| 326 | auto replace_size = record.second.size(); | ||
| 327 | if (record.first + replace_size > in_data.size()) | ||
| 328 | replace_size = in_data.size() - record.first; | ||
| 329 | for (std::size_t i = 0; i < replace_size; ++i) | ||
| 330 | in_data[i + record.first] = record.second[i]; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), | ||
| 335 | in->GetContainingDirectory()); | ||
| 86 | } | 336 | } |
| 87 | 337 | ||
| 88 | } // namespace FileSys | 338 | } // namespace FileSys |
diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h index 81c163494..450b2f71e 100644 --- a/src/core/file_sys/ips_layer.h +++ b/src/core/file_sys/ips_layer.h | |||
| @@ -4,12 +4,41 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 9 | #include <vector> | ||
| 8 | 10 | ||
| 11 | #include "common/common_types.h" | ||
| 9 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs.h" |
| 10 | 13 | ||
| 11 | namespace FileSys { | 14 | namespace FileSys { |
| 12 | 15 | ||
| 13 | VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips); | 16 | VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips); |
| 14 | 17 | ||
| 18 | class IPSwitchCompiler { | ||
| 19 | public: | ||
| 20 | explicit IPSwitchCompiler(VirtualFile patch_text); | ||
| 21 | ~IPSwitchCompiler(); | ||
| 22 | |||
| 23 | std::array<u8, 0x20> GetBuildID() const; | ||
| 24 | bool IsValid() const; | ||
| 25 | VirtualFile Apply(const VirtualFile& in) const; | ||
| 26 | |||
| 27 | private: | ||
| 28 | struct IPSwitchPatch; | ||
| 29 | |||
| 30 | void ParseFlag(const std::string& flag); | ||
| 31 | void Parse(); | ||
| 32 | |||
| 33 | bool valid = false; | ||
| 34 | |||
| 35 | VirtualFile patch_text; | ||
| 36 | std::vector<IPSwitchPatch> patches; | ||
| 37 | std::array<u8, 0x20> nso_build_id{}; | ||
| 38 | bool is_little_endian = false; | ||
| 39 | s64 offset_shift = 0; | ||
| 40 | bool print_values = false; | ||
| 41 | std::string last_comment = ""; | ||
| 42 | }; | ||
| 43 | |||
| 15 | } // namespace FileSys | 44 | } // namespace FileSys |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 1ac00ebb0..019caebe9 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -73,27 +73,38 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 73 | return exefs; | 73 | return exefs; |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | static std::vector<VirtualFile> CollectIPSPatches(const std::vector<VirtualDir>& patch_dirs, | 76 | static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, |
| 77 | const std::string& build_id) { | 77 | const std::string& build_id) { |
| 78 | std::vector<VirtualFile> ips; | 78 | std::vector<VirtualFile> out; |
| 79 | ips.reserve(patch_dirs.size()); | 79 | out.reserve(patch_dirs.size()); |
| 80 | for (const auto& subdir : patch_dirs) { | 80 | for (const auto& subdir : patch_dirs) { |
| 81 | auto exefs_dir = subdir->GetSubdirectory("exefs"); | 81 | auto exefs_dir = subdir->GetSubdirectory("exefs"); |
| 82 | if (exefs_dir != nullptr) { | 82 | if (exefs_dir != nullptr) { |
| 83 | for (const auto& file : exefs_dir->GetFiles()) { | 83 | for (const auto& file : exefs_dir->GetFiles()) { |
| 84 | if (file->GetExtension() != "ips") | 84 | if (file->GetExtension() == "ips") { |
| 85 | continue; | 85 | auto name = file->GetName(); |
| 86 | auto name = file->GetName(); | 86 | const auto p1 = name.substr(0, name.find('.')); |
| 87 | const auto p1 = name.substr(0, name.find('.')); | 87 | const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); |
| 88 | const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); | 88 | |
| 89 | 89 | if (build_id == this_build_id) | |
| 90 | if (build_id == this_build_id) | 90 | out.push_back(file); |
| 91 | ips.push_back(file); | 91 | } else if (file->GetExtension() == "pchtxt") { |
| 92 | IPSwitchCompiler compiler{file}; | ||
| 93 | if (!compiler.IsValid()) | ||
| 94 | continue; | ||
| 95 | |||
| 96 | auto this_build_id = Common::HexArrayToString(compiler.GetBuildID()); | ||
| 97 | this_build_id = | ||
| 98 | this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1); | ||
| 99 | |||
| 100 | if (build_id == this_build_id) | ||
| 101 | out.push_back(file); | ||
| 102 | } | ||
| 92 | } | 103 | } |
| 93 | } | 104 | } |
| 94 | } | 105 | } |
| 95 | 106 | ||
| 96 | return ips; | 107 | return out; |
| 97 | } | 108 | } |
| 98 | 109 | ||
| 99 | std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { | 110 | std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { |
| @@ -115,15 +126,24 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { | |||
| 115 | auto patch_dirs = load_dir->GetSubdirectories(); | 126 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 116 | std::sort(patch_dirs.begin(), patch_dirs.end(), | 127 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 117 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 128 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| 118 | const auto ips = CollectIPSPatches(patch_dirs, build_id); | 129 | const auto patches = CollectPatches(patch_dirs, build_id); |
| 119 | 130 | ||
| 120 | auto out = nso; | 131 | auto out = nso; |
| 121 | for (const auto& ips_file : ips) { | 132 | for (const auto& patch_file : patches) { |
| 122 | LOG_INFO(Loader, " - Appling IPS patch from mod \"{}\"", | 133 | if (patch_file->GetExtension() == "ips") { |
| 123 | ips_file->GetContainingDirectory()->GetParentDirectory()->GetName()); | 134 | LOG_INFO(Loader, " - Applying IPS patch from mod \"{}\"", |
| 124 | const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), ips_file); | 135 | patch_file->GetContainingDirectory()->GetParentDirectory()->GetName()); |
| 125 | if (patched != nullptr) | 136 | const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), patch_file); |
| 126 | out = patched->ReadAllBytes(); | 137 | if (patched != nullptr) |
| 138 | out = patched->ReadAllBytes(); | ||
| 139 | } else if (patch_file->GetExtension() == "pchtxt") { | ||
| 140 | LOG_INFO(Loader, " - Applying IPSwitch patch from mod \"{}\"", | ||
| 141 | patch_file->GetContainingDirectory()->GetParentDirectory()->GetName()); | ||
| 142 | const IPSwitchCompiler compiler{patch_file}; | ||
| 143 | const auto patched = compiler.Apply(std::make_shared<VectorVfsFile>(out)); | ||
| 144 | if (patched != nullptr) | ||
| 145 | out = patched->ReadAllBytes(); | ||
| 146 | } | ||
| 127 | } | 147 | } |
| 128 | 148 | ||
| 129 | if (out.size() < 0x100) | 149 | if (out.size() < 0x100) |
| @@ -143,7 +163,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | |||
| 143 | std::sort(patch_dirs.begin(), patch_dirs.end(), | 163 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 144 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 164 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| 145 | 165 | ||
| 146 | return !CollectIPSPatches(patch_dirs, build_id).empty(); | 166 | return !CollectPatches(patch_dirs, build_id).empty(); |
| 147 | } | 167 | } |
| 148 | 168 | ||
| 149 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | 169 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { |
| @@ -162,11 +182,17 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t | |||
| 162 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 182 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| 163 | 183 | ||
| 164 | std::vector<VirtualDir> layers; | 184 | std::vector<VirtualDir> layers; |
| 185 | std::vector<VirtualDir> layers_ext; | ||
| 165 | layers.reserve(patch_dirs.size() + 1); | 186 | layers.reserve(patch_dirs.size() + 1); |
| 187 | layers_ext.reserve(patch_dirs.size() + 1); | ||
| 166 | for (const auto& subdir : patch_dirs) { | 188 | for (const auto& subdir : patch_dirs) { |
| 167 | auto romfs_dir = subdir->GetSubdirectory("romfs"); | 189 | auto romfs_dir = subdir->GetSubdirectory("romfs"); |
| 168 | if (romfs_dir != nullptr) | 190 | if (romfs_dir != nullptr) |
| 169 | layers.push_back(std::move(romfs_dir)); | 191 | layers.push_back(std::move(romfs_dir)); |
| 192 | |||
| 193 | auto ext_dir = subdir->GetSubdirectory("romfs_ext"); | ||
| 194 | if (ext_dir != nullptr) | ||
| 195 | layers_ext.push_back(std::move(ext_dir)); | ||
| 170 | } | 196 | } |
| 171 | layers.push_back(std::move(extracted)); | 197 | layers.push_back(std::move(extracted)); |
| 172 | 198 | ||
| @@ -175,7 +201,9 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t | |||
| 175 | return; | 201 | return; |
| 176 | } | 202 | } |
| 177 | 203 | ||
| 178 | auto packed = CreateRomFS(std::move(layered)); | 204 | auto layered_ext = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers_ext)); |
| 205 | |||
| 206 | auto packed = CreateRomFS(std::move(layered), std::move(layered_ext)); | ||
| 179 | if (packed == nullptr) { | 207 | if (packed == nullptr) { |
| 180 | return; | 208 | return; |
| 181 | } | 209 | } |
| @@ -263,8 +291,24 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 263 | if (mod_dir != nullptr && mod_dir->GetSize() > 0) { | 291 | if (mod_dir != nullptr && mod_dir->GetSize() > 0) { |
| 264 | for (const auto& mod : mod_dir->GetSubdirectories()) { | 292 | for (const auto& mod : mod_dir->GetSubdirectories()) { |
| 265 | std::string types; | 293 | std::string types; |
| 266 | if (IsDirValidAndNonEmpty(mod->GetSubdirectory("exefs"))) | 294 | |
| 267 | AppendCommaIfNotEmpty(types, "IPS"); | 295 | const auto exefs_dir = mod->GetSubdirectory("exefs"); |
| 296 | if (IsDirValidAndNonEmpty(exefs_dir)) { | ||
| 297 | bool ips = false; | ||
| 298 | bool ipswitch = false; | ||
| 299 | |||
| 300 | for (const auto& file : exefs_dir->GetFiles()) { | ||
| 301 | if (file->GetExtension() == "ips") | ||
| 302 | ips = true; | ||
| 303 | else if (file->GetExtension() == "pchtxt") | ||
| 304 | ipswitch = true; | ||
| 305 | } | ||
| 306 | |||
| 307 | if (ips) | ||
| 308 | AppendCommaIfNotEmpty(types, "IPS"); | ||
| 309 | if (ipswitch) | ||
| 310 | AppendCommaIfNotEmpty(types, "IPSwitch"); | ||
| 311 | } | ||
| 268 | if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) | 312 | if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) |
| 269 | AppendCommaIfNotEmpty(types, "LayeredFS"); | 313 | AppendCommaIfNotEmpty(types, "LayeredFS"); |
| 270 | 314 | ||
| @@ -301,23 +345,22 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 301 | return out; | 345 | return out; |
| 302 | } | 346 | } |
| 303 | 347 | ||
| 304 | std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { | 348 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { |
| 305 | const auto& installed{Service::FileSystem::GetUnionContents()}; | 349 | const auto& installed{Service::FileSystem::GetUnionContents()}; |
| 306 | 350 | ||
| 307 | const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); | 351 | const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); |
| 308 | if (base_control_nca == nullptr) | 352 | if (base_control_nca == nullptr) |
| 309 | return {}; | 353 | return {}; |
| 310 | 354 | ||
| 311 | return ParseControlNCA(base_control_nca); | 355 | return ParseControlNCA(*base_control_nca); |
| 312 | } | 356 | } |
| 313 | 357 | ||
| 314 | std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( | 358 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { |
| 315 | const std::shared_ptr<NCA>& nca) const { | 359 | const auto base_romfs = nca.GetRomFS(); |
| 316 | const auto base_romfs = nca->GetRomFS(); | ||
| 317 | if (base_romfs == nullptr) | 360 | if (base_romfs == nullptr) |
| 318 | return {}; | 361 | return {}; |
| 319 | 362 | ||
| 320 | const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control); | 363 | const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); |
| 321 | if (romfs == nullptr) | 364 | if (romfs == nullptr) |
| 322 | return {}; | 365 | return {}; |
| 323 | 366 | ||
| @@ -329,7 +372,7 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( | |||
| 329 | if (nacp_file == nullptr) | 372 | if (nacp_file == nullptr) |
| 330 | nacp_file = extracted->GetFile("Control.nacp"); | 373 | nacp_file = extracted->GetFile("Control.nacp"); |
| 331 | 374 | ||
| 332 | const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file); | 375 | auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); |
| 333 | 376 | ||
| 334 | VirtualFile icon_file; | 377 | VirtualFile icon_file; |
| 335 | for (const auto& language : FileSys::LANGUAGE_NAMES) { | 378 | for (const auto& language : FileSys::LANGUAGE_NAMES) { |
| @@ -338,6 +381,6 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( | |||
| 338 | break; | 381 | break; |
| 339 | } | 382 | } |
| 340 | 383 | ||
| 341 | return {nacp, icon_file}; | 384 | return {std::move(nacp), icon_file}; |
| 342 | } | 385 | } |
| 343 | } // namespace FileSys | 386 | } // namespace FileSys |
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 2ae9322a1..7d168837f 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -36,6 +36,7 @@ public: | |||
| 36 | 36 | ||
| 37 | // Currently tracked NSO patches: | 37 | // Currently tracked NSO patches: |
| 38 | // - IPS | 38 | // - IPS |
| 39 | // - IPSwitch | ||
| 39 | std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; | 40 | std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; |
| 40 | 41 | ||
| 41 | // Checks to see if PatchNSO() will have any effect given the NSO's build ID. | 42 | // Checks to see if PatchNSO() will have any effect given the NSO's build ID. |
| @@ -56,11 +57,10 @@ public: | |||
| 56 | 57 | ||
| 57 | // Given title_id of the program, attempts to get the control data of the update and parse it, | 58 | // Given title_id of the program, attempts to get the control data of the update and parse it, |
| 58 | // falling back to the base control data. | 59 | // falling back to the base control data. |
| 59 | std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const; | 60 | std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const; |
| 60 | 61 | ||
| 61 | // Version of GetControlMetadata that takes an arbitrary NCA | 62 | // Version of GetControlMetadata that takes an arbitrary NCA |
| 62 | std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA( | 63 | std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; |
| 63 | const std::shared_ptr<NCA>& nca) const; | ||
| 64 | 64 | ||
| 65 | private: | 65 | private: |
| 66 | u64 title_id; | 66 | u64 title_id; |
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 5910f7046..81e1f66ac 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp | |||
| @@ -129,11 +129,11 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { | |||
| 129 | return out; | 129 | return out; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | VirtualFile CreateRomFS(VirtualDir dir) { | 132 | VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { |
| 133 | if (dir == nullptr) | 133 | if (dir == nullptr) |
| 134 | return nullptr; | 134 | return nullptr; |
| 135 | 135 | ||
| 136 | RomFSBuildContext ctx{dir}; | 136 | RomFSBuildContext ctx{dir, ext}; |
| 137 | return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName()); | 137 | return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName()); |
| 138 | } | 138 | } |
| 139 | 139 | ||
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index ecd1eb725..0ec404731 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h | |||
| @@ -44,6 +44,6 @@ VirtualDir ExtractRomFS(VirtualFile file, | |||
| 44 | 44 | ||
| 45 | // Converts a VFS filesystem into a RomFS binary | 45 | // Converts a VFS filesystem into a RomFS binary |
| 46 | // Returns nullptr on failure | 46 | // Returns nullptr on failure |
| 47 | VirtualFile CreateRomFS(VirtualDir dir); | 47 | VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext = nullptr); |
| 48 | 48 | ||
| 49 | } // namespace FileSys | 49 | } // namespace FileSys |
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index e5fa67ae8..885259618 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -22,6 +22,7 @@ enum { | |||
| 22 | HandleTableFull = 105, | 22 | HandleTableFull = 105, |
| 23 | InvalidMemoryState = 106, | 23 | InvalidMemoryState = 106, |
| 24 | InvalidMemoryPermissions = 108, | 24 | InvalidMemoryPermissions = 108, |
| 25 | InvalidMemoryRange = 110, | ||
| 25 | InvalidThreadPriority = 112, | 26 | InvalidThreadPriority = 112, |
| 26 | InvalidProcessorId = 113, | 27 | InvalidProcessorId = 113, |
| 27 | InvalidHandle = 114, | 28 | InvalidHandle = 114, |
| @@ -56,6 +57,7 @@ constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidA | |||
| 56 | constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); | 57 | constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); |
| 57 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, | 58 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, |
| 58 | ErrCodes::InvalidMemoryPermissions); | 59 | ErrCodes::InvalidMemoryPermissions); |
| 60 | constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange); | ||
| 59 | constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); | 61 | constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); |
| 60 | constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); | 62 | constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); |
| 61 | constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); | 63 | constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 98eb74298..bd680adfe 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -116,7 +116,7 @@ struct KernelCore::Impl { | |||
| 116 | next_thread_id = 1; | 116 | next_thread_id = 1; |
| 117 | 117 | ||
| 118 | process_list.clear(); | 118 | process_list.clear(); |
| 119 | current_process.reset(); | 119 | current_process = nullptr; |
| 120 | 120 | ||
| 121 | handle_table.Clear(); | 121 | handle_table.Clear(); |
| 122 | resource_limits.fill(nullptr); | 122 | resource_limits.fill(nullptr); |
| @@ -207,7 +207,7 @@ struct KernelCore::Impl { | |||
| 207 | 207 | ||
| 208 | // Lists all processes that exist in the current session. | 208 | // Lists all processes that exist in the current session. |
| 209 | std::vector<SharedPtr<Process>> process_list; | 209 | std::vector<SharedPtr<Process>> process_list; |
| 210 | SharedPtr<Process> current_process; | 210 | Process* current_process = nullptr; |
| 211 | 211 | ||
| 212 | Kernel::HandleTable handle_table; | 212 | Kernel::HandleTable handle_table; |
| 213 | std::array<SharedPtr<ResourceLimit>, 4> resource_limits; | 213 | std::array<SharedPtr<ResourceLimit>, 4> resource_limits; |
| @@ -266,15 +266,15 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) { | |||
| 266 | impl->process_list.push_back(std::move(process)); | 266 | impl->process_list.push_back(std::move(process)); |
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | void KernelCore::MakeCurrentProcess(SharedPtr<Process> process) { | 269 | void KernelCore::MakeCurrentProcess(Process* process) { |
| 270 | impl->current_process = std::move(process); | 270 | impl->current_process = process; |
| 271 | } | 271 | } |
| 272 | 272 | ||
| 273 | SharedPtr<Process>& KernelCore::CurrentProcess() { | 273 | Process* KernelCore::CurrentProcess() { |
| 274 | return impl->current_process; | 274 | return impl->current_process; |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | const SharedPtr<Process>& KernelCore::CurrentProcess() const { | 277 | const Process* KernelCore::CurrentProcess() const { |
| 278 | return impl->current_process; | 278 | return impl->current_process; |
| 279 | } | 279 | } |
| 280 | 280 | ||
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index c0771ecf0..41554821f 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -66,13 +66,13 @@ public: | |||
| 66 | void AppendNewProcess(SharedPtr<Process> process); | 66 | void AppendNewProcess(SharedPtr<Process> process); |
| 67 | 67 | ||
| 68 | /// Makes the given process the new current process. | 68 | /// Makes the given process the new current process. |
| 69 | void MakeCurrentProcess(SharedPtr<Process> process); | 69 | void MakeCurrentProcess(Process* process); |
| 70 | 70 | ||
| 71 | /// Retrieves a reference to the current process. | 71 | /// Retrieves a pointer to the current process. |
| 72 | SharedPtr<Process>& CurrentProcess(); | 72 | Process* CurrentProcess(); |
| 73 | 73 | ||
| 74 | /// Retrieves a const reference to the current process. | 74 | /// Retrieves a const pointer to the current process. |
| 75 | const SharedPtr<Process>& CurrentProcess() const; | 75 | const Process* CurrentProcess() const; |
| 76 | 76 | ||
| 77 | /// Adds a port to the named port table | 77 | /// Adds a port to the named port table |
| 78 | void AddNamedPort(std::string name, SharedPtr<ClientPort> port); | 78 | void AddNamedPort(std::string name, SharedPtr<ClientPort> port); |
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp index d51562d92..d87a62bb9 100644 --- a/src/core/hle/kernel/object.cpp +++ b/src/core/hle/kernel/object.cpp | |||
| @@ -25,7 +25,6 @@ bool Object::IsWaitable() const { | |||
| 25 | case HandleType::Process: | 25 | case HandleType::Process: |
| 26 | case HandleType::AddressArbiter: | 26 | case HandleType::AddressArbiter: |
| 27 | case HandleType::ResourceLimit: | 27 | case HandleType::ResourceLimit: |
| 28 | case HandleType::CodeSet: | ||
| 29 | case HandleType::ClientPort: | 28 | case HandleType::ClientPort: |
| 30 | case HandleType::ClientSession: | 29 | case HandleType::ClientSession: |
| 31 | return false; | 30 | return false; |
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index 9eb72315c..c9f4d0bb3 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h | |||
| @@ -26,7 +26,6 @@ enum class HandleType : u32 { | |||
| 26 | AddressArbiter, | 26 | AddressArbiter, |
| 27 | Timer, | 27 | Timer, |
| 28 | ResourceLimit, | 28 | ResourceLimit, |
| 29 | CodeSet, | ||
| 30 | ClientPort, | 29 | ClientPort, |
| 31 | ServerPort, | 30 | ServerPort, |
| 32 | ClientSession, | 31 | ClientSession, |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index fb0027a71..c80b2c507 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -20,13 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | namespace Kernel { | 21 | namespace Kernel { |
| 22 | 22 | ||
| 23 | SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) { | 23 | CodeSet::CodeSet() = default; |
| 24 | SharedPtr<CodeSet> codeset(new CodeSet(kernel)); | ||
| 25 | codeset->name = std::move(name); | ||
| 26 | return codeset; | ||
| 27 | } | ||
| 28 | |||
| 29 | CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {} | ||
| 30 | CodeSet::~CodeSet() = default; | 24 | CodeSet::~CodeSet() = default; |
| 31 | 25 | ||
| 32 | SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { | 26 | SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { |
| @@ -224,20 +218,20 @@ void Process::FreeTLSSlot(VAddr tls_address) { | |||
| 224 | tls_slots[tls_page].reset(tls_slot); | 218 | tls_slots[tls_page].reset(tls_slot); |
| 225 | } | 219 | } |
| 226 | 220 | ||
| 227 | void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { | 221 | void Process::LoadModule(CodeSet module_, VAddr base_addr) { |
| 228 | const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, | 222 | const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, |
| 229 | MemoryState memory_state) { | 223 | MemoryState memory_state) { |
| 230 | auto vma = vm_manager | 224 | const auto vma = vm_manager |
| 231 | .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset, | 225 | .MapMemoryBlock(segment.addr + base_addr, module_.memory, |
| 232 | segment.size, memory_state) | 226 | segment.offset, segment.size, memory_state) |
| 233 | .Unwrap(); | 227 | .Unwrap(); |
| 234 | vm_manager.Reprotect(vma, permissions); | 228 | vm_manager.Reprotect(vma, permissions); |
| 235 | }; | 229 | }; |
| 236 | 230 | ||
| 237 | // Map CodeSet segments | 231 | // Map CodeSet segments |
| 238 | MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); | 232 | MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); |
| 239 | MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); | 233 | MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); |
| 240 | MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); | 234 | MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); |
| 241 | } | 235 | } |
| 242 | 236 | ||
| 243 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { | 237 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 590e0c73d..73ec01e11 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -61,26 +61,15 @@ enum class ProcessStatus { Created, Running, Exited }; | |||
| 61 | 61 | ||
| 62 | class ResourceLimit; | 62 | class ResourceLimit; |
| 63 | 63 | ||
| 64 | struct CodeSet final : public Object { | 64 | struct CodeSet final { |
| 65 | struct Segment { | 65 | struct Segment { |
| 66 | std::size_t offset = 0; | 66 | std::size_t offset = 0; |
| 67 | VAddr addr = 0; | 67 | VAddr addr = 0; |
| 68 | u32 size = 0; | 68 | u32 size = 0; |
| 69 | }; | 69 | }; |
| 70 | 70 | ||
| 71 | static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name); | 71 | explicit CodeSet(); |
| 72 | 72 | ~CodeSet(); | |
| 73 | std::string GetTypeName() const override { | ||
| 74 | return "CodeSet"; | ||
| 75 | } | ||
| 76 | std::string GetName() const override { | ||
| 77 | return name; | ||
| 78 | } | ||
| 79 | |||
| 80 | static const HandleType HANDLE_TYPE = HandleType::CodeSet; | ||
| 81 | HandleType GetHandleType() const override { | ||
| 82 | return HANDLE_TYPE; | ||
| 83 | } | ||
| 84 | 73 | ||
| 85 | Segment& CodeSegment() { | 74 | Segment& CodeSegment() { |
| 86 | return segments[0]; | 75 | return segments[0]; |
| @@ -109,14 +98,7 @@ struct CodeSet final : public Object { | |||
| 109 | std::shared_ptr<std::vector<u8>> memory; | 98 | std::shared_ptr<std::vector<u8>> memory; |
| 110 | 99 | ||
| 111 | std::array<Segment, 3> segments; | 100 | std::array<Segment, 3> segments; |
| 112 | VAddr entrypoint; | 101 | VAddr entrypoint = 0; |
| 113 | |||
| 114 | /// Name of the process | ||
| 115 | std::string name; | ||
| 116 | |||
| 117 | private: | ||
| 118 | explicit CodeSet(KernelCore& kernel); | ||
| 119 | ~CodeSet() override; | ||
| 120 | }; | 102 | }; |
| 121 | 103 | ||
| 122 | class Process final : public Object { | 104 | class Process final : public Object { |
| @@ -219,7 +201,7 @@ public: | |||
| 219 | */ | 201 | */ |
| 220 | void PrepareForTermination(); | 202 | void PrepareForTermination(); |
| 221 | 203 | ||
| 222 | void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); | 204 | void LoadModule(CodeSet module_, VAddr base_addr); |
| 223 | 205 | ||
| 224 | /////////////////////////////////////////////////////////////////////////////////////////////// | 206 | /////////////////////////////////////////////////////////////////////////////////////////////// |
| 225 | // Memory Management | 207 | // Memory Management |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index cfd6e1bad..1342c597e 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "core/arm/arm_interface.h" | 10 | #include "core/arm/arm_interface.h" |
| 11 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/core_timing.h" | 12 | #include "core/hle/kernel/kernel.h" |
| 13 | #include "core/hle/kernel/process.h" | 13 | #include "core/hle/kernel/process.h" |
| 14 | #include "core/hle/kernel/scheduler.h" | 14 | #include "core/hle/kernel/scheduler.h" |
| 15 | 15 | ||
| @@ -78,16 +78,16 @@ void Scheduler::SwitchContext(Thread* new_thread) { | |||
| 78 | // Cancel any outstanding wakeup events for this thread | 78 | // Cancel any outstanding wakeup events for this thread |
| 79 | new_thread->CancelWakeupTimer(); | 79 | new_thread->CancelWakeupTimer(); |
| 80 | 80 | ||
| 81 | auto previous_process = Core::CurrentProcess(); | 81 | auto* const previous_process = Core::CurrentProcess(); |
| 82 | 82 | ||
| 83 | current_thread = new_thread; | 83 | current_thread = new_thread; |
| 84 | 84 | ||
| 85 | ready_queue.remove(new_thread->GetPriority(), new_thread); | 85 | ready_queue.remove(new_thread->GetPriority(), new_thread); |
| 86 | new_thread->SetStatus(ThreadStatus::Running); | 86 | new_thread->SetStatus(ThreadStatus::Running); |
| 87 | 87 | ||
| 88 | const auto thread_owner_process = current_thread->GetOwnerProcess(); | 88 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); |
| 89 | if (previous_process != thread_owner_process) { | 89 | if (previous_process != thread_owner_process) { |
| 90 | Core::CurrentProcess() = thread_owner_process; | 90 | Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process); |
| 91 | SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); | 91 | SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); |
| 92 | } | 92 | } |
| 93 | 93 | ||
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 6c4af7e47..e406df829 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -39,6 +39,73 @@ namespace { | |||
| 39 | constexpr bool Is4KBAligned(VAddr address) { | 39 | constexpr bool Is4KBAligned(VAddr address) { |
| 40 | return (address & 0xFFF) == 0; | 40 | return (address & 0xFFF) == 0; |
| 41 | } | 41 | } |
| 42 | |||
| 43 | // Checks if address + size is greater than the given address | ||
| 44 | // This can return false if the size causes an overflow of a 64-bit type | ||
| 45 | // or if the given size is zero. | ||
| 46 | constexpr bool IsValidAddressRange(VAddr address, u64 size) { | ||
| 47 | return address + size > address; | ||
| 48 | } | ||
| 49 | |||
| 50 | // Checks if a given address range lies within a larger address range. | ||
| 51 | constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin, | ||
| 52 | VAddr address_range_end) { | ||
| 53 | const VAddr end_address = address + size - 1; | ||
| 54 | return address_range_begin <= address && end_address <= address_range_end - 1; | ||
| 55 | } | ||
| 56 | |||
| 57 | bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) { | ||
| 58 | return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(), | ||
| 59 | vm.GetAddressSpaceEndAddress()); | ||
| 60 | } | ||
| 61 | |||
| 62 | bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) { | ||
| 63 | return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(), | ||
| 64 | vm.GetNewMapRegionEndAddress()); | ||
| 65 | } | ||
| 66 | |||
| 67 | // Helper function that performs the common sanity checks for svcMapMemory | ||
| 68 | // and svcUnmapMemory. This is doable, as both functions perform their sanitizing | ||
| 69 | // in the same order. | ||
| 70 | ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr, | ||
| 71 | u64 size) { | ||
| 72 | if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { | ||
| 73 | return ERR_INVALID_ADDRESS; | ||
| 74 | } | ||
| 75 | |||
| 76 | if (size == 0 || !Is4KBAligned(size)) { | ||
| 77 | return ERR_INVALID_SIZE; | ||
| 78 | } | ||
| 79 | |||
| 80 | if (!IsValidAddressRange(dst_addr, size)) { | ||
| 81 | return ERR_INVALID_ADDRESS_STATE; | ||
| 82 | } | ||
| 83 | |||
| 84 | if (!IsValidAddressRange(src_addr, size)) { | ||
| 85 | return ERR_INVALID_ADDRESS_STATE; | ||
| 86 | } | ||
| 87 | |||
| 88 | if (!IsInsideAddressSpace(vm_manager, src_addr, size)) { | ||
| 89 | return ERR_INVALID_ADDRESS_STATE; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) { | ||
| 93 | return ERR_INVALID_MEMORY_RANGE; | ||
| 94 | } | ||
| 95 | |||
| 96 | const VAddr dst_end_address = dst_addr + size; | ||
| 97 | if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() && | ||
| 98 | vm_manager.GetHeapRegionEndAddress() > dst_addr) { | ||
| 99 | return ERR_INVALID_MEMORY_RANGE; | ||
| 100 | } | ||
| 101 | |||
| 102 | if (dst_end_address > vm_manager.GetMapRegionBaseAddress() && | ||
| 103 | vm_manager.GetMapRegionEndAddress() > dst_addr) { | ||
| 104 | return ERR_INVALID_MEMORY_RANGE; | ||
| 105 | } | ||
| 106 | |||
| 107 | return RESULT_SUCCESS; | ||
| 108 | } | ||
| 42 | } // Anonymous namespace | 109 | } // Anonymous namespace |
| 43 | 110 | ||
| 44 | /// Set the process heap to a given Size. It can both extend and shrink the heap. | 111 | /// Set the process heap to a given Size. It can both extend and shrink the heap. |
| @@ -69,15 +136,15 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 69 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 136 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 70 | src_addr, size); | 137 | src_addr, size); |
| 71 | 138 | ||
| 72 | if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { | 139 | auto* const current_process = Core::CurrentProcess(); |
| 73 | return ERR_INVALID_ADDRESS; | 140 | const auto& vm_manager = current_process->VMManager(); |
| 74 | } | ||
| 75 | 141 | ||
| 76 | if (size == 0 || !Is4KBAligned(size)) { | 142 | const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); |
| 77 | return ERR_INVALID_SIZE; | 143 | if (result != RESULT_SUCCESS) { |
| 144 | return result; | ||
| 78 | } | 145 | } |
| 79 | 146 | ||
| 80 | return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); | 147 | return current_process->MirrorMemory(dst_addr, src_addr, size); |
| 81 | } | 148 | } |
| 82 | 149 | ||
| 83 | /// Unmaps a region that was previously mapped with svcMapMemory | 150 | /// Unmaps a region that was previously mapped with svcMapMemory |
| @@ -85,15 +152,15 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 85 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 152 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 86 | src_addr, size); | 153 | src_addr, size); |
| 87 | 154 | ||
| 88 | if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { | 155 | auto* const current_process = Core::CurrentProcess(); |
| 89 | return ERR_INVALID_ADDRESS; | 156 | const auto& vm_manager = current_process->VMManager(); |
| 90 | } | ||
| 91 | 157 | ||
| 92 | if (size == 0 || !Is4KBAligned(size)) { | 158 | const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); |
| 93 | return ERR_INVALID_SIZE; | 159 | if (result != RESULT_SUCCESS) { |
| 160 | return result; | ||
| 94 | } | 161 | } |
| 95 | 162 | ||
| 96 | return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); | 163 | return current_process->UnmapMemory(dst_addr, src_addr, size); |
| 97 | } | 164 | } |
| 98 | 165 | ||
| 99 | /// Connect to an OS service given the port name, returns the handle to the port to out | 166 | /// Connect to an OS service given the port name, returns the handle to the port to out |
| @@ -301,13 +368,28 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | |||
| 301 | return Mutex::Release(mutex_addr); | 368 | return Mutex::Release(mutex_addr); |
| 302 | } | 369 | } |
| 303 | 370 | ||
| 371 | struct BreakReason { | ||
| 372 | union { | ||
| 373 | u32 raw; | ||
| 374 | BitField<31, 1, u32> signal_debugger; | ||
| 375 | }; | ||
| 376 | }; | ||
| 377 | |||
| 304 | /// Break program execution | 378 | /// Break program execution |
| 305 | static void Break(u64 reason, u64 info1, u64 info2) { | 379 | static void Break(u32 reason, u64 info1, u64 info2) { |
| 306 | LOG_CRITICAL( | 380 | BreakReason break_reason{reason}; |
| 307 | Debug_Emulated, | 381 | if (break_reason.signal_debugger) { |
| 308 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | 382 | LOG_ERROR( |
| 309 | reason, info1, info2); | 383 | Debug_Emulated, |
| 310 | ASSERT(false); | 384 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", |
| 385 | reason, info1, info2); | ||
| 386 | } else { | ||
| 387 | LOG_CRITICAL( | ||
| 388 | Debug_Emulated, | ||
| 389 | "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", | ||
| 390 | reason, info1, info2); | ||
| 391 | ASSERT(false); | ||
| 392 | } | ||
| 311 | } | 393 | } |
| 312 | 394 | ||
| 313 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | 395 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
| @@ -326,7 +408,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 326 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, | 408 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, |
| 327 | info_sub_id, handle); | 409 | info_sub_id, handle); |
| 328 | 410 | ||
| 329 | const auto& current_process = Core::CurrentProcess(); | 411 | const auto* current_process = Core::CurrentProcess(); |
| 330 | const auto& vm_manager = current_process->VMManager(); | 412 | const auto& vm_manager = current_process->VMManager(); |
| 331 | 413 | ||
| 332 | switch (static_cast<GetInfoType>(info_id)) { | 414 | switch (static_cast<GetInfoType>(info_id)) { |
| @@ -424,7 +506,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | |||
| 424 | return ERR_INVALID_HANDLE; | 506 | return ERR_INVALID_HANDLE; |
| 425 | } | 507 | } |
| 426 | 508 | ||
| 427 | const auto current_process = Core::CurrentProcess(); | 509 | const auto* current_process = Core::CurrentProcess(); |
| 428 | if (thread->GetOwnerProcess() != current_process) { | 510 | if (thread->GetOwnerProcess() != current_process) { |
| 429 | return ERR_INVALID_HANDLE; | 511 | return ERR_INVALID_HANDLE; |
| 430 | } | 512 | } |
| @@ -516,7 +598,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 516 | return ERR_INVALID_HANDLE; | 598 | return ERR_INVALID_HANDLE; |
| 517 | } | 599 | } |
| 518 | 600 | ||
| 519 | return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, | 601 | return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type, |
| 520 | MemoryPermission::DontCare); | 602 | MemoryPermission::DontCare); |
| 521 | } | 603 | } |
| 522 | 604 | ||
| @@ -535,7 +617,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 535 | auto& kernel = Core::System::GetInstance().Kernel(); | 617 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 536 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); | 618 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); |
| 537 | 619 | ||
| 538 | return shared_memory->Unmap(Core::CurrentProcess().get(), addr); | 620 | return shared_memory->Unmap(Core::CurrentProcess(), addr); |
| 539 | } | 621 | } |
| 540 | 622 | ||
| 541 | /// Query process memory | 623 | /// Query process memory |
| @@ -573,7 +655,7 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd | |||
| 573 | 655 | ||
| 574 | /// Exits the current process | 656 | /// Exits the current process |
| 575 | static void ExitProcess() { | 657 | static void ExitProcess() { |
| 576 | auto& current_process = Core::CurrentProcess(); | 658 | auto* current_process = Core::CurrentProcess(); |
| 577 | 659 | ||
| 578 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); | 660 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); |
| 579 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, | 661 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, |
| @@ -621,7 +703,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 621 | auto& kernel = Core::System::GetInstance().Kernel(); | 703 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 622 | CASCADE_RESULT(SharedPtr<Thread> thread, | 704 | CASCADE_RESULT(SharedPtr<Thread> thread, |
| 623 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, | 705 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, |
| 624 | Core::CurrentProcess())); | 706 | *Core::CurrentProcess())); |
| 625 | const auto new_guest_handle = kernel.HandleTable().Create(thread); | 707 | const auto new_guest_handle = kernel.HandleTable().Create(thread); |
| 626 | if (new_guest_handle.Failed()) { | 708 | if (new_guest_handle.Failed()) { |
| 627 | return new_guest_handle.Code(); | 709 | return new_guest_handle.Code(); |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 22712e64f..cbb80c3c4 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -35,18 +35,18 @@ void SvcWrap() { | |||
| 35 | 35 | ||
| 36 | template <ResultCode func(u32)> | 36 | template <ResultCode func(u32)> |
| 37 | void SvcWrap() { | 37 | void SvcWrap() { |
| 38 | FuncReturn(func((u32)Param(0)).raw); | 38 | FuncReturn(func(static_cast<u32>(Param(0))).raw); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | template <ResultCode func(u32, u32)> | 41 | template <ResultCode func(u32, u32)> |
| 42 | void SvcWrap() { | 42 | void SvcWrap() { |
| 43 | FuncReturn(func((u32)Param(0), (u32)Param(1)).raw); | 43 | FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | template <ResultCode func(u32*, u32)> | 46 | template <ResultCode func(u32*, u32)> |
| 47 | void SvcWrap() { | 47 | void SvcWrap() { |
| 48 | u32 param_1 = 0; | 48 | u32 param_1 = 0; |
| 49 | u32 retval = func(¶m_1, (u32)Param(1)).raw; | 49 | u32 retval = func(¶m_1, static_cast<u32>(Param(1))).raw; |
| 50 | Core::CurrentArmInterface().SetReg(1, param_1); | 50 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 51 | FuncReturn(retval); | 51 | FuncReturn(retval); |
| 52 | } | 52 | } |
| @@ -61,7 +61,7 @@ void SvcWrap() { | |||
| 61 | 61 | ||
| 62 | template <ResultCode func(u64, s32)> | 62 | template <ResultCode func(u64, s32)> |
| 63 | void SvcWrap() { | 63 | void SvcWrap() { |
| 64 | FuncReturn(func(Param(0), (s32)Param(1)).raw); | 64 | FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | template <ResultCode func(u64, u32)> | 67 | template <ResultCode func(u64, u32)> |
| @@ -79,19 +79,19 @@ void SvcWrap() { | |||
| 79 | 79 | ||
| 80 | template <ResultCode func(u32, u64)> | 80 | template <ResultCode func(u32, u64)> |
| 81 | void SvcWrap() { | 81 | void SvcWrap() { |
| 82 | FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw); | 82 | FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw); |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | template <ResultCode func(u32, u32, u64)> | 85 | template <ResultCode func(u32, u32, u64)> |
| 86 | void SvcWrap() { | 86 | void SvcWrap() { |
| 87 | FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw); | 87 | FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw); |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | template <ResultCode func(u32, u32*, u64*)> | 90 | template <ResultCode func(u32, u32*, u64*)> |
| 91 | void SvcWrap() { | 91 | void SvcWrap() { |
| 92 | u32 param_1 = 0; | 92 | u32 param_1 = 0; |
| 93 | u64 param_2 = 0; | 93 | u64 param_2 = 0; |
| 94 | ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), ¶m_1, ¶m_2); | 94 | ResultCode retval = func(static_cast<u32>(Param(2)), ¶m_1, ¶m_2); |
| 95 | Core::CurrentArmInterface().SetReg(1, param_1); | 95 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 96 | Core::CurrentArmInterface().SetReg(2, param_2); | 96 | Core::CurrentArmInterface().SetReg(2, param_2); |
| 97 | FuncReturn(retval.raw); | 97 | FuncReturn(retval.raw); |
| @@ -100,12 +100,12 @@ void SvcWrap() { | |||
| 100 | template <ResultCode func(u64, u64, u32, u32)> | 100 | template <ResultCode func(u64, u64, u32, u32)> |
| 101 | void SvcWrap() { | 101 | void SvcWrap() { |
| 102 | FuncReturn( | 102 | FuncReturn( |
| 103 | func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw); | 103 | func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw); |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | template <ResultCode func(u32, u64, u32)> | 106 | template <ResultCode func(u32, u64, u32)> |
| 107 | void SvcWrap() { | 107 | void SvcWrap() { |
| 108 | FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw); | 108 | FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | template <ResultCode func(u64, u64, u64)> | 111 | template <ResultCode func(u64, u64, u64)> |
| @@ -115,25 +115,28 @@ void SvcWrap() { | |||
| 115 | 115 | ||
| 116 | template <ResultCode func(u32, u64, u64, u32)> | 116 | template <ResultCode func(u32, u64, u64, u32)> |
| 117 | void SvcWrap() { | 117 | void SvcWrap() { |
| 118 | FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw); | 118 | FuncReturn( |
| 119 | func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw); | ||
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | template <ResultCode func(u32, u64, u64)> | 122 | template <ResultCode func(u32, u64, u64)> |
| 122 | void SvcWrap() { | 123 | void SvcWrap() { |
| 123 | FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw); | 124 | FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw); |
| 124 | } | 125 | } |
| 125 | 126 | ||
| 126 | template <ResultCode func(u32*, u64, u64, s64)> | 127 | template <ResultCode func(u32*, u64, u64, s64)> |
| 127 | void SvcWrap() { | 128 | void SvcWrap() { |
| 128 | u32 param_1 = 0; | 129 | u32 param_1 = 0; |
| 129 | ResultCode retval = func(¶m_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)); | 130 | ResultCode retval = |
| 131 | func(¶m_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))); | ||
| 130 | Core::CurrentArmInterface().SetReg(1, param_1); | 132 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 131 | FuncReturn(retval.raw); | 133 | FuncReturn(retval.raw); |
| 132 | } | 134 | } |
| 133 | 135 | ||
| 134 | template <ResultCode func(u64, u64, u32, s64)> | 136 | template <ResultCode func(u64, u64, u32, s64)> |
| 135 | void SvcWrap() { | 137 | void SvcWrap() { |
| 136 | FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw); | 138 | FuncReturn( |
| 139 | func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw); | ||
| 137 | } | 140 | } |
| 138 | 141 | ||
| 139 | template <ResultCode func(u64*, u64, u64, u64)> | 142 | template <ResultCode func(u64*, u64, u64, u64)> |
| @@ -147,9 +150,9 @@ void SvcWrap() { | |||
| 147 | template <ResultCode func(u32*, u64, u64, u64, u32, s32)> | 150 | template <ResultCode func(u32*, u64, u64, u64, u32, s32)> |
| 148 | void SvcWrap() { | 151 | void SvcWrap() { |
| 149 | u32 param_1 = 0; | 152 | u32 param_1 = 0; |
| 150 | u32 retval = | 153 | u32 retval = func(¶m_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)), |
| 151 | func(¶m_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF)) | 154 | static_cast<s32>(Param(5))) |
| 152 | .raw; | 155 | .raw; |
| 153 | Core::CurrentArmInterface().SetReg(1, param_1); | 156 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 154 | FuncReturn(retval); | 157 | FuncReturn(retval); |
| 155 | } | 158 | } |
| @@ -172,7 +175,7 @@ void SvcWrap() { | |||
| 172 | template <ResultCode func(u32*, u64, u64, u32)> | 175 | template <ResultCode func(u32*, u64, u64, u32)> |
| 173 | void SvcWrap() { | 176 | void SvcWrap() { |
| 174 | u32 param_1 = 0; | 177 | u32 param_1 = 0; |
| 175 | u32 retval = func(¶m_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw; | 178 | u32 retval = func(¶m_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw; |
| 176 | Core::CurrentArmInterface().SetReg(1, param_1); | 179 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 177 | FuncReturn(retval); | 180 | FuncReturn(retval); |
| 178 | } | 181 | } |
| @@ -181,22 +184,22 @@ template <ResultCode func(Handle*, u64, u32, u32)> | |||
| 181 | void SvcWrap() { | 184 | void SvcWrap() { |
| 182 | u32 param_1 = 0; | 185 | u32 param_1 = 0; |
| 183 | u32 retval = | 186 | u32 retval = |
| 184 | func(¶m_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw; | 187 | func(¶m_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw; |
| 185 | Core::CurrentArmInterface().SetReg(1, param_1); | 188 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 186 | FuncReturn(retval); | 189 | FuncReturn(retval); |
| 187 | } | 190 | } |
| 188 | 191 | ||
| 189 | template <ResultCode func(u64, u32, s32, s64)> | 192 | template <ResultCode func(u64, u32, s32, s64)> |
| 190 | void SvcWrap() { | 193 | void SvcWrap() { |
| 191 | FuncReturn( | 194 | FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)), |
| 192 | func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)) | 195 | static_cast<s64>(Param(3))) |
| 193 | .raw); | 196 | .raw); |
| 194 | } | 197 | } |
| 195 | 198 | ||
| 196 | template <ResultCode func(u64, u32, s32, s32)> | 199 | template <ResultCode func(u64, u32, s32, s32)> |
| 197 | void SvcWrap() { | 200 | void SvcWrap() { |
| 198 | FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), | 201 | FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)), |
| 199 | (s32)(Param(3) & 0xFFFFFFFF)) | 202 | static_cast<s32>(Param(3))) |
| 200 | .raw); | 203 | .raw); |
| 201 | } | 204 | } |
| 202 | 205 | ||
| @@ -226,7 +229,7 @@ void SvcWrap() { | |||
| 226 | 229 | ||
| 227 | template <void func(s64)> | 230 | template <void func(s64)> |
| 228 | void SvcWrap() { | 231 | void SvcWrap() { |
| 229 | func((s64)Param(0)); | 232 | func(static_cast<s64>(Param(0))); |
| 230 | } | 233 | } |
| 231 | 234 | ||
| 232 | template <void func(u64, u64 len)> | 235 | template <void func(u64, u64 len)> |
| @@ -239,4 +242,9 @@ void SvcWrap() { | |||
| 239 | func(Param(0), Param(1), Param(2)); | 242 | func(Param(0), Param(1), Param(2)); |
| 240 | } | 243 | } |
| 241 | 244 | ||
| 245 | template <void func(u32, u64, u64)> | ||
| 246 | void SvcWrap() { | ||
| 247 | func(static_cast<u32>(Param(0)), Param(1), Param(2)); | ||
| 248 | } | ||
| 249 | |||
| 242 | } // namespace Kernel | 250 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 8e514cf9a..352ce1725 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -183,18 +183,15 @@ void Thread::ResumeFromWait() { | |||
| 183 | */ | 183 | */ |
| 184 | static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top, | 184 | static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top, |
| 185 | VAddr entry_point, u64 arg) { | 185 | VAddr entry_point, u64 arg) { |
| 186 | memset(&context, 0, sizeof(Core::ARM_Interface::ThreadContext)); | 186 | context = {}; |
| 187 | |||
| 188 | context.cpu_registers[0] = arg; | 187 | context.cpu_registers[0] = arg; |
| 189 | context.pc = entry_point; | 188 | context.pc = entry_point; |
| 190 | context.sp = stack_top; | 189 | context.sp = stack_top; |
| 191 | context.pstate = 0; | ||
| 192 | context.fpcr = 0; | ||
| 193 | } | 190 | } |
| 194 | 191 | ||
| 195 | ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, | 192 | ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, |
| 196 | u32 priority, u64 arg, s32 processor_id, | 193 | u32 priority, u64 arg, s32 processor_id, |
| 197 | VAddr stack_top, SharedPtr<Process> owner_process) { | 194 | VAddr stack_top, Process& owner_process) { |
| 198 | // Check if priority is in ranged. Lowest priority -> highest priority id. | 195 | // Check if priority is in ranged. Lowest priority -> highest priority id. |
| 199 | if (priority > THREADPRIO_LOWEST) { | 196 | if (priority > THREADPRIO_LOWEST) { |
| 200 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); | 197 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); |
| @@ -208,7 +205,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name | |||
| 208 | 205 | ||
| 209 | // TODO(yuriks): Other checks, returning 0xD9001BEA | 206 | // TODO(yuriks): Other checks, returning 0xD9001BEA |
| 210 | 207 | ||
| 211 | if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { | 208 | if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) { |
| 212 | LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); | 209 | LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); |
| 213 | // TODO (bunnei): Find the correct error code to use here | 210 | // TODO (bunnei): Find the correct error code to use here |
| 214 | return ResultCode(-1); | 211 | return ResultCode(-1); |
| @@ -232,7 +229,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name | |||
| 232 | thread->wait_handle = 0; | 229 | thread->wait_handle = 0; |
| 233 | thread->name = std::move(name); | 230 | thread->name = std::move(name); |
| 234 | thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); | 231 | thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); |
| 235 | thread->owner_process = owner_process; | 232 | thread->owner_process = &owner_process; |
| 236 | thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); | 233 | thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); |
| 237 | thread->scheduler->AddThread(thread, priority); | 234 | thread->scheduler->AddThread(thread, priority); |
| 238 | thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); | 235 | thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); |
| @@ -264,7 +261,7 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri | |||
| 264 | // Initialize new "main" thread | 261 | // Initialize new "main" thread |
| 265 | const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); | 262 | const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); |
| 266 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, | 263 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, |
| 267 | stack_top, &owner_process); | 264 | stack_top, owner_process); |
| 268 | 265 | ||
| 269 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); | 266 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); |
| 270 | 267 | ||
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index c6ffbd28c..f4d7bd235 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -89,7 +89,7 @@ public: | |||
| 89 | static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name, | 89 | static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name, |
| 90 | VAddr entry_point, u32 priority, u64 arg, | 90 | VAddr entry_point, u32 priority, u64 arg, |
| 91 | s32 processor_id, VAddr stack_top, | 91 | s32 processor_id, VAddr stack_top, |
| 92 | SharedPtr<Process> owner_process); | 92 | Process& owner_process); |
| 93 | 93 | ||
| 94 | std::string GetName() const override { | 94 | std::string GetName() const override { |
| 95 | return name; | 95 | return name; |
| @@ -262,11 +262,11 @@ public: | |||
| 262 | return processor_id; | 262 | return processor_id; |
| 263 | } | 263 | } |
| 264 | 264 | ||
| 265 | SharedPtr<Process>& GetOwnerProcess() { | 265 | Process* GetOwnerProcess() { |
| 266 | return owner_process; | 266 | return owner_process; |
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | const SharedPtr<Process>& GetOwnerProcess() const { | 269 | const Process* GetOwnerProcess() const { |
| 270 | return owner_process; | 270 | return owner_process; |
| 271 | } | 271 | } |
| 272 | 272 | ||
| @@ -386,7 +386,7 @@ private: | |||
| 386 | u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register. | 386 | u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register. |
| 387 | 387 | ||
| 388 | /// Process that owns this thread | 388 | /// Process that owns this thread |
| 389 | SharedPtr<Process> owner_process; | 389 | Process* owner_process; |
| 390 | 390 | ||
| 391 | /// Objects that the thread is waiting on, in the same order as they were | 391 | /// Objects that the thread is waiting on, in the same order as they were |
| 392 | /// passed to WaitSynchronization1/N. | 392 | /// passed to WaitSynchronization1/N. |
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 79580bcd9..0ecfb5af1 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -61,13 +61,13 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs | |||
| 61 | AOC_U::~AOC_U() = default; | 61 | AOC_U::~AOC_U() = default; |
| 62 | 62 | ||
| 63 | void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { | 63 | void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { |
| 64 | IPC::ResponseBuilder rb{ctx, 4}; | 64 | IPC::ResponseBuilder rb{ctx, 3}; |
| 65 | rb.Push(RESULT_SUCCESS); | 65 | rb.Push(RESULT_SUCCESS); |
| 66 | 66 | ||
| 67 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 67 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 68 | rb.Push<u32>(std::count_if(add_on_content.begin(), add_on_content.end(), [¤t](u64 tid) { | 68 | rb.Push<u32>(static_cast<u32>( |
| 69 | return (tid & DLC_BASE_TITLE_ID_MASK) == current; | 69 | std::count_if(add_on_content.begin(), add_on_content.end(), |
| 70 | })); | 70 | [¤t](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | 73 | void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { |
| @@ -91,7 +91,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 91 | return; | 91 | return; |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | count = std::min<size_t>(out.size() - offset, count); | 94 | count = static_cast<u32>(std::min<size_t>(out.size() - offset, count)); |
| 95 | std::rotate(out.begin(), out.begin() + offset, out.end()); | 95 | std::rotate(out.begin(), out.begin() + offset, out.end()); |
| 96 | out.resize(count); | 96 | out.resize(count); |
| 97 | 97 | ||
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index fc6067e59..7168c6a10 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -2,8 +2,10 @@ | |||
| 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 <chrono> | ||
| 5 | #include <cstring> | 6 | #include <cstring> |
| 6 | #include <memory> | 7 | #include <memory> |
| 8 | #include <optional> | ||
| 7 | #include <vector> | 9 | #include <vector> |
| 8 | 10 | ||
| 9 | #include <opus.h> | 11 | #include <opus.h> |
| @@ -33,7 +35,8 @@ public: | |||
| 33 | {1, nullptr, "SetContext"}, | 35 | {1, nullptr, "SetContext"}, |
| 34 | {2, nullptr, "DecodeInterleavedForMultiStream"}, | 36 | {2, nullptr, "DecodeInterleavedForMultiStream"}, |
| 35 | {3, nullptr, "SetContextForMultiStream"}, | 37 | {3, nullptr, "SetContextForMultiStream"}, |
| 36 | {4, nullptr, "Unknown4"}, | 38 | {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerformance, |
| 39 | "DecodeInterleavedWithPerformance"}, | ||
| 37 | {5, nullptr, "Unknown5"}, | 40 | {5, nullptr, "Unknown5"}, |
| 38 | {6, nullptr, "Unknown6"}, | 41 | {6, nullptr, "Unknown6"}, |
| 39 | {7, nullptr, "Unknown7"}, | 42 | {7, nullptr, "Unknown7"}, |
| @@ -59,8 +62,31 @@ private: | |||
| 59 | ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); | 62 | ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); |
| 60 | } | 63 | } |
| 61 | 64 | ||
| 62 | bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, | 65 | void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) { |
| 63 | std::vector<opus_int16>& output) { | 66 | u32 consumed = 0; |
| 67 | u32 sample_count = 0; | ||
| 68 | u64 performance = 0; | ||
| 69 | std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); | ||
| 70 | if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples, | ||
| 71 | performance)) { | ||
| 72 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 73 | // TODO(ogniK): Use correct error code | ||
| 74 | rb.Push(ResultCode(-1)); | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 78 | rb.Push(RESULT_SUCCESS); | ||
| 79 | rb.Push<u32>(consumed); | ||
| 80 | rb.Push<u64>(performance); | ||
| 81 | rb.Push<u32>(sample_count); | ||
| 82 | ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); | ||
| 83 | } | ||
| 84 | |||
| 85 | bool Decoder_DecodeInterleaved( | ||
| 86 | u32& consumed, u32& sample_count, const std::vector<u8>& input, | ||
| 87 | std::vector<opus_int16>& output, | ||
| 88 | std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) { | ||
| 89 | const auto start_time = std::chrono::high_resolution_clock::now(); | ||
| 64 | std::size_t raw_output_sz = output.size() * sizeof(opus_int16); | 90 | std::size_t raw_output_sz = output.size() * sizeof(opus_int16); |
| 65 | if (sizeof(OpusHeader) > input.size()) | 91 | if (sizeof(OpusHeader) > input.size()) |
| 66 | return false; | 92 | return false; |
| @@ -80,8 +106,13 @@ private: | |||
| 80 | (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0); | 106 | (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0); |
| 81 | if (out_sample_count < 0) | 107 | if (out_sample_count < 0) |
| 82 | return false; | 108 | return false; |
| 109 | const auto end_time = std::chrono::high_resolution_clock::now() - start_time; | ||
| 83 | sample_count = out_sample_count; | 110 | sample_count = out_sample_count; |
| 84 | consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); | 111 | consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); |
| 112 | if (performance_time.has_value()) { | ||
| 113 | performance_time->get() = | ||
| 114 | std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count(); | ||
| 115 | } | ||
| 85 | return true; | 116 | return true; |
| 86 | } | 117 | } |
| 87 | 118 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index d8b8037a8..c41ef7058 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -15,6 +15,11 @@ | |||
| 15 | #include "video_core/renderer_base.h" | 15 | #include "video_core/renderer_base.h" |
| 16 | 16 | ||
| 17 | namespace Service::Nvidia::Devices { | 17 | namespace Service::Nvidia::Devices { |
| 18 | namespace NvErrCodes { | ||
| 19 | enum { | ||
| 20 | InvalidNmapHandle = -22, | ||
| 21 | }; | ||
| 22 | } | ||
| 18 | 23 | ||
| 19 | nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} | 24 | nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} |
| 20 | nvhost_as_gpu::~nvhost_as_gpu() = default; | 25 | nvhost_as_gpu::~nvhost_as_gpu() = default; |
| @@ -79,14 +84,16 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 79 | std::memcpy(entries.data(), input.data(), input.size()); | 84 | std::memcpy(entries.data(), input.data(), input.size()); |
| 80 | 85 | ||
| 81 | auto& gpu = Core::System::GetInstance().GPU(); | 86 | auto& gpu = Core::System::GetInstance().GPU(); |
| 82 | |||
| 83 | for (const auto& entry : entries) { | 87 | for (const auto& entry : entries) { |
| 84 | LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", | 88 | LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", |
| 85 | entry.offset, entry.nvmap_handle, entry.pages); | 89 | entry.offset, entry.nvmap_handle, entry.pages); |
| 86 | Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; | 90 | Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; |
| 87 | |||
| 88 | auto object = nvmap_dev->GetObject(entry.nvmap_handle); | 91 | auto object = nvmap_dev->GetObject(entry.nvmap_handle); |
| 89 | ASSERT(object); | 92 | if (!object) { |
| 93 | LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle); | ||
| 94 | std::memcpy(output.data(), entries.data(), output.size()); | ||
| 95 | return static_cast<u32>(NvErrCodes::InvalidNmapHandle); | ||
| 96 | } | ||
| 90 | 97 | ||
| 91 | ASSERT(object->status == nvmap::Object::Status::Allocated); | 98 | ASSERT(object->status == nvmap::Object::Status::Allocated); |
| 92 | 99 | ||
| @@ -157,15 +164,21 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 157 | LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); | 164 | LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); |
| 158 | 165 | ||
| 159 | const auto itr = buffer_mappings.find(params.offset); | 166 | const auto itr = buffer_mappings.find(params.offset); |
| 160 | ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); | 167 | if (itr == buffer_mappings.end()) { |
| 168 | LOG_WARNING(Service_NVDRV, "Tried to unmap an invalid offset 0x{:X}", params.offset); | ||
| 169 | // Hardware tests shows that unmapping an already unmapped buffer always returns successful | ||
| 170 | // and doesn't fail. | ||
| 171 | return 0; | ||
| 172 | } | ||
| 161 | 173 | ||
| 162 | auto& system_instance = Core::System::GetInstance(); | 174 | auto& system_instance = Core::System::GetInstance(); |
| 163 | 175 | ||
| 164 | // Remove this memory region from the rasterizer cache. | 176 | // Remove this memory region from the rasterizer cache. |
| 165 | system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset, | ||
| 166 | itr->second.size); | ||
| 167 | |||
| 168 | auto& gpu = system_instance.GPU(); | 177 | auto& gpu = system_instance.GPU(); |
| 178 | auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset); | ||
| 179 | ASSERT(cpu_addr); | ||
| 180 | system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size); | ||
| 181 | |||
| 169 | params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); | 182 | params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); |
| 170 | 183 | ||
| 171 | buffer_mappings.erase(itr->second.offset); | 184 | buffer_mappings.erase(itr->second.offset); |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index a2287cc1b..43651d8a6 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp | |||
| @@ -11,6 +11,13 @@ | |||
| 11 | 11 | ||
| 12 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 13 | 13 | ||
| 14 | namespace NvErrCodes { | ||
| 15 | enum { | ||
| 16 | OperationNotPermitted = -1, | ||
| 17 | InvalidValue = -22, | ||
| 18 | }; | ||
| 19 | } | ||
| 20 | |||
| 14 | nvmap::nvmap() = default; | 21 | nvmap::nvmap() = default; |
| 15 | nvmap::~nvmap() = default; | 22 | nvmap::~nvmap() = default; |
| 16 | 23 | ||
| @@ -44,7 +51,11 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o | |||
| 44 | u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { | 51 | u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { |
| 45 | IocCreateParams params; | 52 | IocCreateParams params; |
| 46 | std::memcpy(¶ms, input.data(), sizeof(params)); | 53 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 54 | LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); | ||
| 47 | 55 | ||
| 56 | if (!params.size) { | ||
| 57 | return static_cast<u32>(NvErrCodes::InvalidValue); | ||
| 58 | } | ||
| 48 | // Create a new nvmap object and obtain a handle to it. | 59 | // Create a new nvmap object and obtain a handle to it. |
| 49 | auto object = std::make_shared<Object>(); | 60 | auto object = std::make_shared<Object>(); |
| 50 | object->id = next_id++; | 61 | object->id = next_id++; |
| @@ -55,8 +66,6 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 55 | u32 handle = next_handle++; | 66 | u32 handle = next_handle++; |
| 56 | handles[handle] = std::move(object); | 67 | handles[handle] = std::move(object); |
| 57 | 68 | ||
| 58 | LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); | ||
| 59 | |||
| 60 | params.handle = handle; | 69 | params.handle = handle; |
| 61 | 70 | ||
| 62 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 71 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| @@ -66,9 +75,29 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 66 | u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { | 75 | u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { |
| 67 | IocAllocParams params; | 76 | IocAllocParams params; |
| 68 | std::memcpy(¶ms, input.data(), sizeof(params)); | 77 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 78 | LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); | ||
| 79 | |||
| 80 | if (!params.handle) { | ||
| 81 | return static_cast<u32>(NvErrCodes::InvalidValue); | ||
| 82 | } | ||
| 83 | |||
| 84 | if ((params.align - 1) & params.align) { | ||
| 85 | return static_cast<u32>(NvErrCodes::InvalidValue); | ||
| 86 | } | ||
| 87 | |||
| 88 | const u32 min_alignment = 0x1000; | ||
| 89 | if (params.align < min_alignment) { | ||
| 90 | params.align = min_alignment; | ||
| 91 | } | ||
| 69 | 92 | ||
| 70 | auto object = GetObject(params.handle); | 93 | auto object = GetObject(params.handle); |
| 71 | ASSERT(object); | 94 | if (!object) { |
| 95 | return static_cast<u32>(NvErrCodes::InvalidValue); | ||
| 96 | } | ||
| 97 | |||
| 98 | if (object->status == Object::Status::Allocated) { | ||
| 99 | return static_cast<u32>(NvErrCodes::OperationNotPermitted); | ||
| 100 | } | ||
| 72 | 101 | ||
| 73 | object->flags = params.flags; | 102 | object->flags = params.flags; |
| 74 | object->align = params.align; | 103 | object->align = params.align; |
| @@ -76,8 +105,6 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 76 | object->addr = params.addr; | 105 | object->addr = params.addr; |
| 77 | object->status = Object::Status::Allocated; | 106 | object->status = Object::Status::Allocated; |
| 78 | 107 | ||
| 79 | LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); | ||
| 80 | |||
| 81 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 108 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 82 | return 0; | 109 | return 0; |
| 83 | } | 110 | } |
| @@ -88,8 +115,14 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 88 | 115 | ||
| 89 | LOG_WARNING(Service_NVDRV, "called"); | 116 | LOG_WARNING(Service_NVDRV, "called"); |
| 90 | 117 | ||
| 118 | if (!params.handle) { | ||
| 119 | return static_cast<u32>(NvErrCodes::InvalidValue); | ||
| 120 | } | ||
| 121 | |||
| 91 | auto object = GetObject(params.handle); | 122 | auto object = GetObject(params.handle); |
| 92 | ASSERT(object); | 123 | if (!object) { |
| 124 | return static_cast<u32>(NvErrCodes::OperationNotPermitted); | ||
| 125 | } | ||
| 93 | 126 | ||
| 94 | params.id = object->id; | 127 | params.id = object->id; |
| 95 | 128 | ||
| @@ -105,7 +138,14 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 105 | 138 | ||
| 106 | auto itr = std::find_if(handles.begin(), handles.end(), | 139 | auto itr = std::find_if(handles.begin(), handles.end(), |
| 107 | [&](const auto& entry) { return entry.second->id == params.id; }); | 140 | [&](const auto& entry) { return entry.second->id == params.id; }); |
| 108 | ASSERT(itr != handles.end()); | 141 | if (itr == handles.end()) { |
| 142 | return static_cast<u32>(NvErrCodes::InvalidValue); | ||
| 143 | } | ||
| 144 | |||
| 145 | auto& object = itr->second; | ||
| 146 | if (object->status != Object::Status::Allocated) { | ||
| 147 | return static_cast<u32>(NvErrCodes::InvalidValue); | ||
| 148 | } | ||
| 109 | 149 | ||
| 110 | itr->second->refcount++; | 150 | itr->second->refcount++; |
| 111 | 151 | ||
| @@ -125,8 +165,13 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 125 | LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); | 165 | LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); |
| 126 | 166 | ||
| 127 | auto object = GetObject(params.handle); | 167 | auto object = GetObject(params.handle); |
| 128 | ASSERT(object); | 168 | if (!object) { |
| 129 | ASSERT(object->status == Object::Status::Allocated); | 169 | return static_cast<u32>(NvErrCodes::InvalidValue); |
| 170 | } | ||
| 171 | |||
| 172 | if (object->status != Object::Status::Allocated) { | ||
| 173 | return static_cast<u32>(NvErrCodes::OperationNotPermitted); | ||
| 174 | } | ||
| 130 | 175 | ||
| 131 | switch (static_cast<ParamTypes>(params.param)) { | 176 | switch (static_cast<ParamTypes>(params.param)) { |
| 132 | case ParamTypes::Size: | 177 | case ParamTypes::Size: |
| @@ -163,9 +208,12 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 163 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | 208 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); |
| 164 | 209 | ||
| 165 | auto itr = handles.find(params.handle); | 210 | auto itr = handles.find(params.handle); |
| 166 | ASSERT(itr != handles.end()); | 211 | if (itr == handles.end()) { |
| 167 | 212 | return static_cast<u32>(NvErrCodes::InvalidValue); | |
| 168 | ASSERT(itr->second->refcount > 0); | 213 | } |
| 214 | if (!itr->second->refcount) { | ||
| 215 | return static_cast<u32>(NvErrCodes::InvalidValue); | ||
| 216 | } | ||
| 169 | 217 | ||
| 170 | itr->second->refcount--; | 218 | itr->second->refcount--; |
| 171 | 219 | ||
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 9a86e5824..951fd8257 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.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 <cinttypes> | 5 | #include <cinttypes> |
| 6 | #include <cstring> | ||
| 6 | #include "common/common_funcs.h" | 7 | #include "common/common_funcs.h" |
| 7 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 8 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| @@ -140,7 +141,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) | |||
| 140 | const FileSys::VirtualFile module_file = dir->GetFile(module); | 141 | const FileSys::VirtualFile module_file = dir->GetFile(module); |
| 141 | if (module_file != nullptr) { | 142 | if (module_file != nullptr) { |
| 142 | const VAddr load_addr = next_load_addr; | 143 | const VAddr load_addr = next_load_addr; |
| 143 | next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, pm); | 144 | next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, |
| 145 | std::strcmp(module, "rtld") == 0, pm); | ||
| 144 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 146 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); |
| 145 | // Register module with GDBStub | 147 | // Register module with GDBStub |
| 146 | GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); | 148 | GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index e67b49fc9..6057c7f26 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -9,16 +9,11 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/file_util.h" | 10 | #include "common/file_util.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "core/core.h" | ||
| 13 | #include "core/hle/kernel/kernel.h" | ||
| 14 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 15 | #include "core/hle/kernel/vm_manager.h" | 13 | #include "core/hle/kernel/vm_manager.h" |
| 16 | #include "core/loader/elf.h" | 14 | #include "core/loader/elf.h" |
| 17 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 18 | 16 | ||
| 19 | using Kernel::CodeSet; | ||
| 20 | using Kernel::SharedPtr; | ||
| 21 | |||
| 22 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 23 | // ELF Header Constants | 18 | // ELF Header Constants |
| 24 | 19 | ||
| @@ -211,7 +206,7 @@ public: | |||
| 211 | u32 GetFlags() const { | 206 | u32 GetFlags() const { |
| 212 | return (u32)(header->e_flags); | 207 | return (u32)(header->e_flags); |
| 213 | } | 208 | } |
| 214 | SharedPtr<CodeSet> LoadInto(VAddr vaddr); | 209 | Kernel::CodeSet LoadInto(VAddr vaddr); |
| 215 | 210 | ||
| 216 | int GetNumSegments() const { | 211 | int GetNumSegments() const { |
| 217 | return (int)(header->e_phnum); | 212 | return (int)(header->e_phnum); |
| @@ -274,7 +269,7 @@ const char* ElfReader::GetSectionName(int section) const { | |||
| 274 | return nullptr; | 269 | return nullptr; |
| 275 | } | 270 | } |
| 276 | 271 | ||
| 277 | SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) { | 272 | Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) { |
| 278 | LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); | 273 | LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); |
| 279 | 274 | ||
| 280 | // Should we relocate? | 275 | // Should we relocate? |
| @@ -302,8 +297,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) { | |||
| 302 | std::vector<u8> program_image(total_image_size); | 297 | std::vector<u8> program_image(total_image_size); |
| 303 | std::size_t current_image_position = 0; | 298 | std::size_t current_image_position = 0; |
| 304 | 299 | ||
| 305 | auto& kernel = Core::System::GetInstance().Kernel(); | 300 | Kernel::CodeSet codeset; |
| 306 | SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, ""); | ||
| 307 | 301 | ||
| 308 | for (unsigned int i = 0; i < header->e_phnum; ++i) { | 302 | for (unsigned int i = 0; i < header->e_phnum; ++i) { |
| 309 | const Elf32_Phdr* p = &segments[i]; | 303 | const Elf32_Phdr* p = &segments[i]; |
| @@ -311,14 +305,14 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) { | |||
| 311 | p->p_vaddr, p->p_filesz, p->p_memsz); | 305 | p->p_vaddr, p->p_filesz, p->p_memsz); |
| 312 | 306 | ||
| 313 | if (p->p_type == PT_LOAD) { | 307 | if (p->p_type == PT_LOAD) { |
| 314 | CodeSet::Segment* codeset_segment; | 308 | Kernel::CodeSet::Segment* codeset_segment; |
| 315 | u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); | 309 | u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); |
| 316 | if (permission_flags == (PF_R | PF_X)) { | 310 | if (permission_flags == (PF_R | PF_X)) { |
| 317 | codeset_segment = &codeset->CodeSegment(); | 311 | codeset_segment = &codeset.CodeSegment(); |
| 318 | } else if (permission_flags == (PF_R)) { | 312 | } else if (permission_flags == (PF_R)) { |
| 319 | codeset_segment = &codeset->RODataSegment(); | 313 | codeset_segment = &codeset.RODataSegment(); |
| 320 | } else if (permission_flags == (PF_R | PF_W)) { | 314 | } else if (permission_flags == (PF_R | PF_W)) { |
| 321 | codeset_segment = &codeset->DataSegment(); | 315 | codeset_segment = &codeset.DataSegment(); |
| 322 | } else { | 316 | } else { |
| 323 | LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, | 317 | LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, |
| 324 | p->p_flags); | 318 | p->p_flags); |
| @@ -345,8 +339,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) { | |||
| 345 | } | 339 | } |
| 346 | } | 340 | } |
| 347 | 341 | ||
| 348 | codeset->entrypoint = base_addr + header->e_entry; | 342 | codeset.entrypoint = base_addr + header->e_entry; |
| 349 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 343 | codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
| 350 | 344 | ||
| 351 | LOG_DEBUG(Loader, "Done loading."); | 345 | LOG_DEBUG(Loader, "Done loading."); |
| 352 | 346 | ||
| @@ -397,11 +391,11 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) { | |||
| 397 | 391 | ||
| 398 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | 392 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); |
| 399 | ElfReader elf_reader(&buffer[0]); | 393 | ElfReader elf_reader(&buffer[0]); |
| 400 | SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address); | 394 | Kernel::CodeSet codeset = elf_reader.LoadInto(base_address); |
| 401 | codeset->name = file->GetName(); | 395 | const VAddr entry_point = codeset.entrypoint; |
| 402 | 396 | ||
| 403 | process.LoadModule(codeset, codeset->entrypoint); | 397 | process.LoadModule(std::move(codeset), entry_point); |
| 404 | process.Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE); | 398 | process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE); |
| 405 | 399 | ||
| 406 | is_loaded = true; | 400 | is_loaded = true; |
| 407 | return ResultStatus::Success; | 401 | return ResultStatus::Success; |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index c10f826a4..576fe692a 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -14,11 +14,12 @@ | |||
| 14 | #include "core/file_sys/control_metadata.h" | 14 | #include "core/file_sys/control_metadata.h" |
| 15 | #include "core/file_sys/vfs_offset.h" | 15 | #include "core/file_sys/vfs_offset.h" |
| 16 | #include "core/gdbstub/gdbstub.h" | 16 | #include "core/gdbstub/gdbstub.h" |
| 17 | #include "core/hle/kernel/kernel.h" | ||
| 18 | #include "core/hle/kernel/process.h" | 17 | #include "core/hle/kernel/process.h" |
| 19 | #include "core/hle/kernel/vm_manager.h" | 18 | #include "core/hle/kernel/vm_manager.h" |
| 20 | #include "core/loader/nro.h" | 19 | #include "core/loader/nro.h" |
| 20 | #include "core/loader/nso.h" | ||
| 21 | #include "core/memory.h" | 21 | #include "core/memory.h" |
| 22 | #include "core/settings.h" | ||
| 22 | 23 | ||
| 23 | namespace Loader { | 24 | namespace Loader { |
| 24 | 25 | ||
| @@ -137,17 +138,29 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { | |||
| 137 | } | 138 | } |
| 138 | 139 | ||
| 139 | // Build program image | 140 | // Build program image |
| 140 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 141 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, ""); | ||
| 142 | std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); | 141 | std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); |
| 143 | if (program_image.size() != PageAlignSize(nro_header.file_size)) { | 142 | if (program_image.size() != PageAlignSize(nro_header.file_size)) { |
| 144 | return {}; | 143 | return {}; |
| 145 | } | 144 | } |
| 146 | 145 | ||
| 146 | Kernel::CodeSet codeset; | ||
| 147 | for (std::size_t i = 0; i < nro_header.segments.size(); ++i) { | 147 | for (std::size_t i = 0; i < nro_header.segments.size(); ++i) { |
| 148 | codeset->segments[i].addr = nro_header.segments[i].offset; | 148 | codeset.segments[i].addr = nro_header.segments[i].offset; |
| 149 | codeset->segments[i].offset = nro_header.segments[i].offset; | 149 | codeset.segments[i].offset = nro_header.segments[i].offset; |
| 150 | codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size); | 150 | codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size); |
| 151 | } | ||
| 152 | |||
| 153 | if (!Settings::values.program_args.empty()) { | ||
| 154 | const auto arg_data = Settings::values.program_args; | ||
| 155 | codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; | ||
| 156 | NSOArgumentHeader args_header{ | ||
| 157 | NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; | ||
| 158 | const auto end_offset = program_image.size(); | ||
| 159 | program_image.resize(static_cast<u32>(program_image.size()) + | ||
| 160 | NSO_ARGUMENT_DATA_ALLOCATION_SIZE); | ||
| 161 | std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader)); | ||
| 162 | std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(), | ||
| 163 | arg_data.size()); | ||
| 151 | } | 164 | } |
| 152 | 165 | ||
| 153 | // Read MOD header | 166 | // Read MOD header |
| @@ -161,16 +174,15 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { | |||
| 161 | // Resize program image to include .bss section and page align each section | 174 | // Resize program image to include .bss section and page align each section |
| 162 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); | 175 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); |
| 163 | } | 176 | } |
| 164 | codeset->DataSegment().size += bss_size; | 177 | codeset.DataSegment().size += bss_size; |
| 165 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); | 178 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); |
| 166 | 179 | ||
| 167 | // Load codeset for current process | 180 | // Load codeset for current process |
| 168 | codeset->name = file->GetName(); | 181 | codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
| 169 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 182 | Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); |
| 170 | Core::CurrentProcess()->LoadModule(codeset, load_base); | ||
| 171 | 183 | ||
| 172 | // Register module with GDBStub | 184 | // Register module with GDBStub |
| 173 | GDBStub::RegisterModule(codeset->name, load_base, load_base); | 185 | GDBStub::RegisterModule(file->GetName(), load_base, load_base); |
| 174 | 186 | ||
| 175 | return true; | 187 | return true; |
| 176 | } | 188 | } |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 2186b02af..ba57db9bf 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -12,11 +12,11 @@ | |||
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/file_sys/patch_manager.h" | 13 | #include "core/file_sys/patch_manager.h" |
| 14 | #include "core/gdbstub/gdbstub.h" | 14 | #include "core/gdbstub/gdbstub.h" |
| 15 | #include "core/hle/kernel/kernel.h" | ||
| 16 | #include "core/hle/kernel/process.h" | 15 | #include "core/hle/kernel/process.h" |
| 17 | #include "core/hle/kernel/vm_manager.h" | 16 | #include "core/hle/kernel/vm_manager.h" |
| 18 | #include "core/loader/nso.h" | 17 | #include "core/loader/nso.h" |
| 19 | #include "core/memory.h" | 18 | #include "core/memory.h" |
| 19 | #include "core/settings.h" | ||
| 20 | 20 | ||
| 21 | namespace Loader { | 21 | namespace Loader { |
| 22 | 22 | ||
| @@ -94,6 +94,7 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, | 96 | VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, |
| 97 | bool should_pass_arguments, | ||
| 97 | boost::optional<FileSys::PatchManager> pm) { | 98 | boost::optional<FileSys::PatchManager> pm) { |
| 98 | if (file == nullptr) | 99 | if (file == nullptr) |
| 99 | return {}; | 100 | return {}; |
| @@ -109,8 +110,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, | |||
| 109 | return {}; | 110 | return {}; |
| 110 | 111 | ||
| 111 | // Build program image | 112 | // Build program image |
| 112 | auto& kernel = Core::System::GetInstance().Kernel(); | 113 | Kernel::CodeSet codeset; |
| 113 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, ""); | ||
| 114 | std::vector<u8> program_image; | 114 | std::vector<u8> program_image; |
| 115 | for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { | 115 | for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { |
| 116 | std::vector<u8> data = | 116 | std::vector<u8> data = |
| @@ -120,9 +120,22 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, | |||
| 120 | } | 120 | } |
| 121 | program_image.resize(nso_header.segments[i].location); | 121 | program_image.resize(nso_header.segments[i].location); |
| 122 | program_image.insert(program_image.end(), data.begin(), data.end()); | 122 | program_image.insert(program_image.end(), data.begin(), data.end()); |
| 123 | codeset->segments[i].addr = nso_header.segments[i].location; | 123 | codeset.segments[i].addr = nso_header.segments[i].location; |
| 124 | codeset->segments[i].offset = nso_header.segments[i].location; | 124 | codeset.segments[i].offset = nso_header.segments[i].location; |
| 125 | codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); | 125 | codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size())); |
| 126 | } | ||
| 127 | |||
| 128 | if (should_pass_arguments && !Settings::values.program_args.empty()) { | ||
| 129 | const auto arg_data = Settings::values.program_args; | ||
| 130 | codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; | ||
| 131 | NSOArgumentHeader args_header{ | ||
| 132 | NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; | ||
| 133 | const auto end_offset = program_image.size(); | ||
| 134 | program_image.resize(static_cast<u32>(program_image.size()) + | ||
| 135 | NSO_ARGUMENT_DATA_ALLOCATION_SIZE); | ||
| 136 | std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader)); | ||
| 137 | std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(), | ||
| 138 | arg_data.size()); | ||
| 126 | } | 139 | } |
| 127 | 140 | ||
| 128 | // MOD header pointer is at .text offset + 4 | 141 | // MOD header pointer is at .text offset + 4 |
| @@ -139,7 +152,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, | |||
| 139 | // Resize program image to include .bss section and page align each section | 152 | // Resize program image to include .bss section and page align each section |
| 140 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); | 153 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); |
| 141 | } | 154 | } |
| 142 | codeset->DataSegment().size += bss_size; | 155 | codeset.DataSegment().size += bss_size; |
| 143 | const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; | 156 | const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; |
| 144 | program_image.resize(image_size); | 157 | program_image.resize(image_size); |
| 145 | 158 | ||
| @@ -155,12 +168,11 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, | |||
| 155 | } | 168 | } |
| 156 | 169 | ||
| 157 | // Load codeset for current process | 170 | // Load codeset for current process |
| 158 | codeset->name = file->GetName(); | 171 | codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
| 159 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 172 | Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); |
| 160 | Core::CurrentProcess()->LoadModule(codeset, load_base); | ||
| 161 | 173 | ||
| 162 | // Register module with GDBStub | 174 | // Register module with GDBStub |
| 163 | GDBStub::RegisterModule(codeset->name, load_base, load_base); | 175 | GDBStub::RegisterModule(file->GetName(), load_base, load_base); |
| 164 | 176 | ||
| 165 | return load_base + image_size; | 177 | return load_base + image_size; |
| 166 | } | 178 | } |
| @@ -172,7 +184,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) { | |||
| 172 | 184 | ||
| 173 | // Load module | 185 | // Load module |
| 174 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | 186 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); |
| 175 | LoadModule(file, base_address); | 187 | LoadModule(file, base_address, true); |
| 176 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); | 188 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); |
| 177 | 189 | ||
| 178 | process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); | 190 | process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); |
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 05353d4d9..70ab3b718 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -11,6 +11,15 @@ | |||
| 11 | 11 | ||
| 12 | namespace Loader { | 12 | namespace Loader { |
| 13 | 13 | ||
| 14 | constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; | ||
| 15 | |||
| 16 | struct NSOArgumentHeader { | ||
| 17 | u32_le allocated_size; | ||
| 18 | u32_le actual_size; | ||
| 19 | INSERT_PADDING_BYTES(0x18); | ||
| 20 | }; | ||
| 21 | static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size."); | ||
| 22 | |||
| 14 | /// Loads an NSO file | 23 | /// Loads an NSO file |
| 15 | class AppLoader_NSO final : public AppLoader, Linker { | 24 | class AppLoader_NSO final : public AppLoader, Linker { |
| 16 | public: | 25 | public: |
| @@ -27,7 +36,7 @@ public: | |||
| 27 | return IdentifyType(file); | 36 | return IdentifyType(file); |
| 28 | } | 37 | } |
| 29 | 38 | ||
| 30 | static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, | 39 | static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments, |
| 31 | boost::optional<FileSys::PatchManager> pm = boost::none); | 40 | boost::optional<FileSys::PatchManager> pm = boost::none); |
| 32 | 41 | ||
| 33 | ResultStatus Load(Kernel::Process& process) override; | 42 | ResultStatus Load(Kernel::Process& process) override; |
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 5534ce01c..13e57848d 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp | |||
| @@ -35,7 +35,7 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) | |||
| 35 | return; | 35 | return; |
| 36 | 36 | ||
| 37 | std::tie(nacp_file, icon_file) = | 37 | std::tie(nacp_file, icon_file) = |
| 38 | FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(control_nca); | 38 | FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | AppLoader_NSP::~AppLoader_NSP() = default; | 41 | AppLoader_NSP::~AppLoader_NSP() = default; |
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index b006594a6..db91cd01e 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h | |||
| @@ -49,7 +49,7 @@ private: | |||
| 49 | std::unique_ptr<AppLoader> secondary_loader; | 49 | std::unique_ptr<AppLoader> secondary_loader; |
| 50 | 50 | ||
| 51 | FileSys::VirtualFile icon_file; | 51 | FileSys::VirtualFile icon_file; |
| 52 | std::shared_ptr<FileSys::NACP> nacp_file; | 52 | std::unique_ptr<FileSys::NACP> nacp_file; |
| 53 | u64 title_id; | 53 | u64 title_id; |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index ee5452eb9..7a619acb4 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp | |||
| @@ -30,7 +30,7 @@ AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) | |||
| 30 | return; | 30 | return; |
| 31 | 31 | ||
| 32 | std::tie(nacp_file, icon_file) = | 32 | std::tie(nacp_file, icon_file) = |
| 33 | FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(control_nca); | 33 | FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | AppLoader_XCI::~AppLoader_XCI() = default; | 36 | AppLoader_XCI::~AppLoader_XCI() = default; |
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 770ed1437..46f8dfc9e 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h | |||
| @@ -49,7 +49,7 @@ private: | |||
| 49 | std::unique_ptr<AppLoader_NCA> nca_loader; | 49 | std::unique_ptr<AppLoader_NCA> nca_loader; |
| 50 | 50 | ||
| 51 | FileSys::VirtualFile icon_file; | 51 | FileSys::VirtualFile icon_file; |
| 52 | std::shared_ptr<FileSys::NACP> nacp_file; | 52 | std::unique_ptr<FileSys::NACP> nacp_file; |
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| 55 | } // namespace Loader | 55 | } // namespace Loader |
diff --git a/src/core/settings.h b/src/core/settings.h index 1808f5937..83b9a04c8 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -155,6 +155,7 @@ struct Values { | |||
| 155 | // Debugging | 155 | // Debugging |
| 156 | bool use_gdbstub; | 156 | bool use_gdbstub; |
| 157 | u16 gdbstub_port; | 157 | u16 gdbstub_port; |
| 158 | std::string program_args; | ||
| 158 | 159 | ||
| 159 | // WebService | 160 | // WebService |
| 160 | bool enable_telemetry; | 161 | bool enable_telemetry; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index f29fff1e7..7b04792b5 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -2,12 +2,16 @@ | |||
| 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 <array> | ||
| 6 | |||
| 7 | #include <mbedtls/ctr_drbg.h> | ||
| 8 | #include <mbedtls/entropy.h> | ||
| 9 | |||
| 5 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 6 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 7 | #include "common/file_util.h" | 12 | #include "common/file_util.h" |
| 13 | #include "common/logging/log.h" | ||
| 8 | 14 | ||
| 9 | #include <mbedtls/ctr_drbg.h> | ||
| 10 | #include <mbedtls/entropy.h> | ||
| 11 | #include "core/core.h" | 15 | #include "core/core.h" |
| 12 | #include "core/file_sys/control_metadata.h" | 16 | #include "core/file_sys/control_metadata.h" |
| 13 | #include "core/file_sys/patch_manager.h" | 17 | #include "core/file_sys/patch_manager.h" |
| @@ -28,11 +32,11 @@ static u64 GenerateTelemetryId() { | |||
| 28 | mbedtls_entropy_context entropy; | 32 | mbedtls_entropy_context entropy; |
| 29 | mbedtls_entropy_init(&entropy); | 33 | mbedtls_entropy_init(&entropy); |
| 30 | mbedtls_ctr_drbg_context ctr_drbg; | 34 | mbedtls_ctr_drbg_context ctr_drbg; |
| 31 | std::string personalization = "yuzu Telemetry ID"; | 35 | constexpr std::array<char, 18> personalization{{"yuzu Telemetry ID"}}; |
| 32 | 36 | ||
| 33 | mbedtls_ctr_drbg_init(&ctr_drbg); | 37 | mbedtls_ctr_drbg_init(&ctr_drbg); |
| 34 | ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, | 38 | ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, |
| 35 | reinterpret_cast<const unsigned char*>(personalization.c_str()), | 39 | reinterpret_cast<const unsigned char*>(personalization.data()), |
| 36 | personalization.size()) == 0); | 40 | personalization.size()) == 0); |
| 37 | ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id), | 41 | ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id), |
| 38 | sizeof(u64)) == 0); | 42 | sizeof(u64)) == 0); |
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index cec271df0..2a4845797 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | ||
| 8 | #include "common/telemetry.h" | 9 | #include "common/telemetry.h" |
| 9 | 10 | ||
| 10 | namespace Core { | 11 | namespace Core { |
| @@ -30,8 +31,6 @@ public: | |||
| 30 | field_collection.AddField(type, name, std::move(value)); | 31 | field_collection.AddField(type, name, std::move(value)); |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | static void FinalizeAsyncJob(); | ||
| 34 | |||
| 35 | private: | 34 | private: |
| 36 | Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session | 35 | Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session |
| 37 | std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields | 36 | std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields |
| @@ -53,7 +52,6 @@ u64 RegenerateTelemetryId(); | |||
| 53 | * Verifies the username and token. | 52 | * Verifies the username and token. |
| 54 | * @param username yuzu username to use for authentication. | 53 | * @param username yuzu username to use for authentication. |
| 55 | * @param token yuzu token to use for authentication. | 54 | * @param token yuzu token to use for authentication. |
| 56 | * @param func A function that gets exectued when the verification is finished | ||
| 57 | * @returns Future with bool indicating whether the verification succeeded | 55 | * @returns Future with bool indicating whether the verification succeeded |
| 58 | */ | 56 | */ |
| 59 | bool VerifyLogin(const std::string& username, const std::string& token); | 57 | bool VerifyLogin(const std::string& username, const std::string& token); |
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index c0a57e71f..37e15bad0 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp | |||
| @@ -15,7 +15,8 @@ namespace ArmTests { | |||
| 15 | TestEnvironment::TestEnvironment(bool mutable_memory_) | 15 | TestEnvironment::TestEnvironment(bool mutable_memory_) |
| 16 | : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { | 16 | : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { |
| 17 | 17 | ||
| 18 | Core::CurrentProcess() = Kernel::Process::Create(kernel, ""); | 18 | auto process = Kernel::Process::Create(kernel, ""); |
| 19 | kernel.MakeCurrentProcess(process.get()); | ||
| 19 | page_table = &Core::CurrentProcess()->VMManager().page_table; | 20 | page_table = &Core::CurrentProcess()->VMManager().page_table; |
| 20 | 21 | ||
| 21 | std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); | 22 | std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); |
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index 81d15c62a..2a6e8bbbb 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h | |||
| @@ -36,9 +36,9 @@ public: | |||
| 36 | RenderTargetFormat format; | 36 | RenderTargetFormat format; |
| 37 | BitField<0, 1, u32> linear; | 37 | BitField<0, 1, u32> linear; |
| 38 | union { | 38 | union { |
| 39 | BitField<0, 4, u32> block_depth; | 39 | BitField<0, 4, u32> block_width; |
| 40 | BitField<4, 4, u32> block_height; | 40 | BitField<4, 4, u32> block_height; |
| 41 | BitField<8, 4, u32> block_width; | 41 | BitField<8, 4, u32> block_depth; |
| 42 | }; | 42 | }; |
| 43 | u32 depth; | 43 | u32 depth; |
| 44 | u32 layer; | 44 | u32 layer; |
| @@ -53,10 +53,20 @@ public: | |||
| 53 | address_low); | 53 | address_low); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | u32 BlockWidth() const { | ||
| 57 | // The block width is stored in log2 format. | ||
| 58 | return 1 << block_width; | ||
| 59 | } | ||
| 60 | |||
| 56 | u32 BlockHeight() const { | 61 | u32 BlockHeight() const { |
| 57 | // The block height is stored in log2 format. | 62 | // The block height is stored in log2 format. |
| 58 | return 1 << block_height; | 63 | return 1 << block_height; |
| 59 | } | 64 | } |
| 65 | |||
| 66 | u32 BlockDepth() const { | ||
| 67 | // The block depth is stored in log2 format. | ||
| 68 | return 1 << block_depth; | ||
| 69 | } | ||
| 60 | }; | 70 | }; |
| 61 | static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size"); | 71 | static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size"); |
| 62 | 72 | ||
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 4290da33f..c8d1b6478 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -347,6 +347,16 @@ public: | |||
| 347 | DecrWrap = 8, | 347 | DecrWrap = 8, |
| 348 | }; | 348 | }; |
| 349 | 349 | ||
| 350 | enum class MemoryLayout : u32 { | ||
| 351 | Linear = 0, | ||
| 352 | BlockLinear = 1, | ||
| 353 | }; | ||
| 354 | |||
| 355 | enum class InvMemoryLayout : u32 { | ||
| 356 | BlockLinear = 0, | ||
| 357 | Linear = 1, | ||
| 358 | }; | ||
| 359 | |||
| 350 | struct Cull { | 360 | struct Cull { |
| 351 | enum class FrontFace : u32 { | 361 | enum class FrontFace : u32 { |
| 352 | ClockWise = 0x0900, | 362 | ClockWise = 0x0900, |
| @@ -432,7 +442,12 @@ public: | |||
| 432 | u32 width; | 442 | u32 width; |
| 433 | u32 height; | 443 | u32 height; |
| 434 | Tegra::RenderTargetFormat format; | 444 | Tegra::RenderTargetFormat format; |
| 435 | u32 block_dimensions; | 445 | union { |
| 446 | BitField<0, 3, u32> block_width; | ||
| 447 | BitField<4, 3, u32> block_height; | ||
| 448 | BitField<8, 3, u32> block_depth; | ||
| 449 | BitField<12, 1, InvMemoryLayout> type; | ||
| 450 | } memory_layout; | ||
| 436 | u32 array_mode; | 451 | u32 array_mode; |
| 437 | u32 layer_stride; | 452 | u32 layer_stride; |
| 438 | u32 base_layer; | 453 | u32 base_layer; |
| @@ -532,7 +547,21 @@ public: | |||
| 532 | INSERT_PADDING_WORDS(0x3); | 547 | INSERT_PADDING_WORDS(0x3); |
| 533 | s32 clear_stencil; | 548 | s32 clear_stencil; |
| 534 | 549 | ||
| 535 | INSERT_PADDING_WORDS(0x6C); | 550 | INSERT_PADDING_WORDS(0x17); |
| 551 | |||
| 552 | struct { | ||
| 553 | u32 enable; | ||
| 554 | union { | ||
| 555 | BitField<0, 16, u32> min_x; | ||
| 556 | BitField<16, 16, u32> max_x; | ||
| 557 | }; | ||
| 558 | union { | ||
| 559 | BitField<0, 16, u32> min_y; | ||
| 560 | BitField<16, 16, u32> max_y; | ||
| 561 | }; | ||
| 562 | } scissor_test; | ||
| 563 | |||
| 564 | INSERT_PADDING_WORDS(0x52); | ||
| 536 | 565 | ||
| 537 | s32 stencil_back_func_ref; | 566 | s32 stencil_back_func_ref; |
| 538 | u32 stencil_back_mask; | 567 | u32 stencil_back_mask; |
| @@ -548,7 +577,12 @@ public: | |||
| 548 | u32 address_high; | 577 | u32 address_high; |
| 549 | u32 address_low; | 578 | u32 address_low; |
| 550 | Tegra::DepthFormat format; | 579 | Tegra::DepthFormat format; |
| 551 | u32 block_dimensions; | 580 | union { |
| 581 | BitField<0, 4, u32> block_width; | ||
| 582 | BitField<4, 4, u32> block_height; | ||
| 583 | BitField<8, 4, u32> block_depth; | ||
| 584 | BitField<20, 1, InvMemoryLayout> type; | ||
| 585 | } memory_layout; | ||
| 552 | u32 layer_stride; | 586 | u32 layer_stride; |
| 553 | 587 | ||
| 554 | GPUVAddr Address() const { | 588 | GPUVAddr Address() const { |
| @@ -1002,6 +1036,7 @@ ASSERT_REG_POSITION(vertex_buffer, 0x35D); | |||
| 1002 | ASSERT_REG_POSITION(clear_color[0], 0x360); | 1036 | ASSERT_REG_POSITION(clear_color[0], 0x360); |
| 1003 | ASSERT_REG_POSITION(clear_depth, 0x364); | 1037 | ASSERT_REG_POSITION(clear_depth, 0x364); |
| 1004 | ASSERT_REG_POSITION(clear_stencil, 0x368); | 1038 | ASSERT_REG_POSITION(clear_stencil, 0x368); |
| 1039 | ASSERT_REG_POSITION(scissor_test, 0x380); | ||
| 1005 | ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); | 1040 | ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); |
| 1006 | ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); | 1041 | ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); |
| 1007 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); | 1042 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index b1f137b9c..9a59b65b3 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -214,6 +214,18 @@ enum class IMinMaxExchange : u64 { | |||
| 214 | XHi = 3, | 214 | XHi = 3, |
| 215 | }; | 215 | }; |
| 216 | 216 | ||
| 217 | enum class VmadType : u64 { | ||
| 218 | Size16_Low = 0, | ||
| 219 | Size16_High = 1, | ||
| 220 | Size32 = 2, | ||
| 221 | Invalid = 3, | ||
| 222 | }; | ||
| 223 | |||
| 224 | enum class VmadShr : u64 { | ||
| 225 | Shr7 = 1, | ||
| 226 | Shr15 = 2, | ||
| 227 | }; | ||
| 228 | |||
| 217 | enum class XmadMode : u64 { | 229 | enum class XmadMode : u64 { |
| 218 | None = 0, | 230 | None = 0, |
| 219 | CLo = 1, | 231 | CLo = 1, |
| @@ -314,6 +326,15 @@ enum class TextureMiscMode : u64 { | |||
| 314 | PTP, | 326 | PTP, |
| 315 | }; | 327 | }; |
| 316 | 328 | ||
| 329 | enum class IsberdMode : u64 { | ||
| 330 | None = 0, | ||
| 331 | Patch = 1, | ||
| 332 | Prim = 2, | ||
| 333 | Attr = 3, | ||
| 334 | }; | ||
| 335 | |||
| 336 | enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 }; | ||
| 337 | |||
| 317 | enum class IpaInterpMode : u64 { | 338 | enum class IpaInterpMode : u64 { |
| 318 | Linear = 0, | 339 | Linear = 0, |
| 319 | Perspective = 1, | 340 | Perspective = 1, |
| @@ -340,6 +361,87 @@ struct IpaMode { | |||
| 340 | } | 361 | } |
| 341 | }; | 362 | }; |
| 342 | 363 | ||
| 364 | enum class SystemVariable : u64 { | ||
| 365 | LaneId = 0x00, | ||
| 366 | VirtCfg = 0x02, | ||
| 367 | VirtId = 0x03, | ||
| 368 | Pm0 = 0x04, | ||
| 369 | Pm1 = 0x05, | ||
| 370 | Pm2 = 0x06, | ||
| 371 | Pm3 = 0x07, | ||
| 372 | Pm4 = 0x08, | ||
| 373 | Pm5 = 0x09, | ||
| 374 | Pm6 = 0x0a, | ||
| 375 | Pm7 = 0x0b, | ||
| 376 | OrderingTicket = 0x0f, | ||
| 377 | PrimType = 0x10, | ||
| 378 | InvocationId = 0x11, | ||
| 379 | Ydirection = 0x12, | ||
| 380 | ThreadKill = 0x13, | ||
| 381 | ShaderType = 0x14, | ||
| 382 | DirectBeWriteAddressLow = 0x15, | ||
| 383 | DirectBeWriteAddressHigh = 0x16, | ||
| 384 | DirectBeWriteEnabled = 0x17, | ||
| 385 | MachineId0 = 0x18, | ||
| 386 | MachineId1 = 0x19, | ||
| 387 | MachineId2 = 0x1a, | ||
| 388 | MachineId3 = 0x1b, | ||
| 389 | Affinity = 0x1c, | ||
| 390 | InvocationInfo = 0x1d, | ||
| 391 | WscaleFactorXY = 0x1e, | ||
| 392 | WscaleFactorZ = 0x1f, | ||
| 393 | Tid = 0x20, | ||
| 394 | TidX = 0x21, | ||
| 395 | TidY = 0x22, | ||
| 396 | TidZ = 0x23, | ||
| 397 | CtaParam = 0x24, | ||
| 398 | CtaIdX = 0x25, | ||
| 399 | CtaIdY = 0x26, | ||
| 400 | CtaIdZ = 0x27, | ||
| 401 | NtId = 0x28, | ||
| 402 | CirQueueIncrMinusOne = 0x29, | ||
| 403 | Nlatc = 0x2a, | ||
| 404 | SmSpaVersion = 0x2c, | ||
| 405 | MultiPassShaderInfo = 0x2d, | ||
| 406 | LwinHi = 0x2e, | ||
| 407 | SwinHi = 0x2f, | ||
| 408 | SwinLo = 0x30, | ||
| 409 | SwinSz = 0x31, | ||
| 410 | SmemSz = 0x32, | ||
| 411 | SmemBanks = 0x33, | ||
| 412 | LwinLo = 0x34, | ||
| 413 | LwinSz = 0x35, | ||
| 414 | LmemLosz = 0x36, | ||
| 415 | LmemHioff = 0x37, | ||
| 416 | EqMask = 0x38, | ||
| 417 | LtMask = 0x39, | ||
| 418 | LeMask = 0x3a, | ||
| 419 | GtMask = 0x3b, | ||
| 420 | GeMask = 0x3c, | ||
| 421 | RegAlloc = 0x3d, | ||
| 422 | CtxAddr = 0x3e, // .fmask = F_SM50 | ||
| 423 | BarrierAlloc = 0x3e, // .fmask = F_SM60 | ||
| 424 | GlobalErrorStatus = 0x40, | ||
| 425 | WarpErrorStatus = 0x42, | ||
| 426 | WarpErrorStatusClear = 0x43, | ||
| 427 | PmHi0 = 0x48, | ||
| 428 | PmHi1 = 0x49, | ||
| 429 | PmHi2 = 0x4a, | ||
| 430 | PmHi3 = 0x4b, | ||
| 431 | PmHi4 = 0x4c, | ||
| 432 | PmHi5 = 0x4d, | ||
| 433 | PmHi6 = 0x4e, | ||
| 434 | PmHi7 = 0x4f, | ||
| 435 | ClockLo = 0x50, | ||
| 436 | ClockHi = 0x51, | ||
| 437 | GlobalTimerLo = 0x52, | ||
| 438 | GlobalTimerHi = 0x53, | ||
| 439 | HwTaskId = 0x60, | ||
| 440 | CircularQueueEntryIndex = 0x61, | ||
| 441 | CircularQueueEntryAddressLow = 0x62, | ||
| 442 | CircularQueueEntryAddressHigh = 0x63, | ||
| 443 | }; | ||
| 444 | |||
| 343 | union Instruction { | 445 | union Instruction { |
| 344 | Instruction& operator=(const Instruction& instr) { | 446 | Instruction& operator=(const Instruction& instr) { |
| 345 | value = instr.value; | 447 | value = instr.value; |
| @@ -362,6 +464,7 @@ union Instruction { | |||
| 362 | BitField<48, 16, u64> opcode; | 464 | BitField<48, 16, u64> opcode; |
| 363 | 465 | ||
| 364 | union { | 466 | union { |
| 467 | BitField<20, 16, u64> imm20_16; | ||
| 365 | BitField<20, 19, u64> imm20_19; | 468 | BitField<20, 19, u64> imm20_19; |
| 366 | BitField<20, 32, s64> imm20_32; | 469 | BitField<20, 32, s64> imm20_32; |
| 367 | BitField<45, 1, u64> negate_b; | 470 | BitField<45, 1, u64> negate_b; |
| @@ -403,6 +506,10 @@ union Instruction { | |||
| 403 | } | 506 | } |
| 404 | } lop3; | 507 | } lop3; |
| 405 | 508 | ||
| 509 | u16 GetImm20_16() const { | ||
| 510 | return static_cast<u16>(imm20_16); | ||
| 511 | } | ||
| 512 | |||
| 406 | u32 GetImm20_19() const { | 513 | u32 GetImm20_19() const { |
| 407 | u32 imm{static_cast<u32>(imm20_19)}; | 514 | u32 imm{static_cast<u32>(imm20_19)}; |
| 408 | imm <<= 12; | 515 | imm <<= 12; |
| @@ -915,6 +1022,35 @@ union Instruction { | |||
| 915 | } bra; | 1022 | } bra; |
| 916 | 1023 | ||
| 917 | union { | 1024 | union { |
| 1025 | BitField<39, 1, u64> emit; // EmitVertex | ||
| 1026 | BitField<40, 1, u64> cut; // EndPrimitive | ||
| 1027 | } out; | ||
| 1028 | |||
| 1029 | union { | ||
| 1030 | BitField<31, 1, u64> skew; | ||
| 1031 | BitField<32, 1, u64> o; | ||
| 1032 | BitField<33, 2, IsberdMode> mode; | ||
| 1033 | BitField<47, 2, IsberdShift> shift; | ||
| 1034 | } isberd; | ||
| 1035 | |||
| 1036 | union { | ||
| 1037 | BitField<48, 1, u64> signed_a; | ||
| 1038 | BitField<38, 1, u64> is_byte_chunk_a; | ||
| 1039 | BitField<36, 2, VmadType> type_a; | ||
| 1040 | BitField<36, 2, u64> byte_height_a; | ||
| 1041 | |||
| 1042 | BitField<49, 1, u64> signed_b; | ||
| 1043 | BitField<50, 1, u64> use_register_b; | ||
| 1044 | BitField<30, 1, u64> is_byte_chunk_b; | ||
| 1045 | BitField<28, 2, VmadType> type_b; | ||
| 1046 | BitField<28, 2, u64> byte_height_b; | ||
| 1047 | |||
| 1048 | BitField<51, 2, VmadShr> shr; | ||
| 1049 | BitField<55, 1, u64> saturate; // Saturates the result (a * b + c) | ||
| 1050 | BitField<47, 1, u64> cc; | ||
| 1051 | } vmad; | ||
| 1052 | |||
| 1053 | union { | ||
| 918 | BitField<20, 16, u64> imm20_16; | 1054 | BitField<20, 16, u64> imm20_16; |
| 919 | BitField<36, 1, u64> product_shift_left; | 1055 | BitField<36, 1, u64> product_shift_left; |
| 920 | BitField<37, 1, u64> merge_37; | 1056 | BitField<37, 1, u64> merge_37; |
| @@ -936,6 +1072,10 @@ union Instruction { | |||
| 936 | BitField<36, 5, u64> index; | 1072 | BitField<36, 5, u64> index; |
| 937 | } cbuf36; | 1073 | } cbuf36; |
| 938 | 1074 | ||
| 1075 | // Unsure about the size of this one. | ||
| 1076 | // It's always used with a gpr0, so any size should be fine. | ||
| 1077 | BitField<20, 8, SystemVariable> sys20; | ||
| 1078 | |||
| 939 | BitField<47, 1, u64> generates_cc; | 1079 | BitField<47, 1, u64> generates_cc; |
| 940 | BitField<61, 1, u64> is_b_imm; | 1080 | BitField<61, 1, u64> is_b_imm; |
| 941 | BitField<60, 1, u64> is_b_gpr; | 1081 | BitField<60, 1, u64> is_b_gpr; |
| @@ -975,6 +1115,9 @@ public: | |||
| 975 | TMML, // Texture Mip Map Level | 1115 | TMML, // Texture Mip Map Level |
| 976 | EXIT, | 1116 | EXIT, |
| 977 | IPA, | 1117 | IPA, |
| 1118 | OUT_R, // Emit vertex/primitive | ||
| 1119 | ISBERD, | ||
| 1120 | VMAD, | ||
| 978 | FFMA_IMM, // Fused Multiply and Add | 1121 | FFMA_IMM, // Fused Multiply and Add |
| 979 | FFMA_CR, | 1122 | FFMA_CR, |
| 980 | FFMA_RC, | 1123 | FFMA_RC, |
| @@ -1034,6 +1177,7 @@ public: | |||
| 1034 | MOV_C, | 1177 | MOV_C, |
| 1035 | MOV_R, | 1178 | MOV_R, |
| 1036 | MOV_IMM, | 1179 | MOV_IMM, |
| 1180 | MOV_SYS, | ||
| 1037 | MOV32_IMM, | 1181 | MOV32_IMM, |
| 1038 | SHL_C, | 1182 | SHL_C, |
| 1039 | SHL_R, | 1183 | SHL_R, |
| @@ -1209,6 +1353,9 @@ private: | |||
| 1209 | INST("1101111101011---", Id::TMML, Type::Memory, "TMML"), | 1353 | INST("1101111101011---", Id::TMML, Type::Memory, "TMML"), |
| 1210 | INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), | 1354 | INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), |
| 1211 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), | 1355 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), |
| 1356 | INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), | ||
| 1357 | INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), | ||
| 1358 | INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"), | ||
| 1212 | INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), | 1359 | INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), |
| 1213 | INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), | 1360 | INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), |
| 1214 | INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), | 1361 | INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), |
| @@ -1255,6 +1402,7 @@ private: | |||
| 1255 | INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), | 1402 | INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), |
| 1256 | INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), | 1403 | INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), |
| 1257 | INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), | 1404 | INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), |
| 1405 | INST("1111000011001---", Id::MOV_SYS, Type::Trivial, "MOV_SYS"), | ||
| 1258 | INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"), | 1406 | INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"), |
| 1259 | INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), | 1407 | INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), |
| 1260 | INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), | 1408 | INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 209bdf181..84582c777 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -255,7 +255,7 @@ DrawParameters RasterizerOpenGL::SetupDraw() { | |||
| 255 | return params; | 255 | return params; |
| 256 | } | 256 | } |
| 257 | 257 | ||
| 258 | void RasterizerOpenGL::SetupShaders() { | 258 | void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { |
| 259 | MICROPROFILE_SCOPE(OpenGL_Shader); | 259 | MICROPROFILE_SCOPE(OpenGL_Shader); |
| 260 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | 260 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); |
| 261 | 261 | ||
| @@ -270,6 +270,11 @@ void RasterizerOpenGL::SetupShaders() { | |||
| 270 | 270 | ||
| 271 | // Skip stages that are not enabled | 271 | // Skip stages that are not enabled |
| 272 | if (!gpu.regs.IsShaderConfigEnabled(index)) { | 272 | if (!gpu.regs.IsShaderConfigEnabled(index)) { |
| 273 | switch (program) { | ||
| 274 | case Maxwell::ShaderProgram::Geometry: | ||
| 275 | shader_program_manager->UseTrivialGeometryShader(); | ||
| 276 | break; | ||
| 277 | } | ||
| 273 | continue; | 278 | continue; |
| 274 | } | 279 | } |
| 275 | 280 | ||
| @@ -288,11 +293,18 @@ void RasterizerOpenGL::SetupShaders() { | |||
| 288 | switch (program) { | 293 | switch (program) { |
| 289 | case Maxwell::ShaderProgram::VertexA: | 294 | case Maxwell::ShaderProgram::VertexA: |
| 290 | case Maxwell::ShaderProgram::VertexB: { | 295 | case Maxwell::ShaderProgram::VertexB: { |
| 291 | shader_program_manager->UseProgrammableVertexShader(shader->GetProgramHandle()); | 296 | shader_program_manager->UseProgrammableVertexShader( |
| 297 | shader->GetProgramHandle(primitive_mode)); | ||
| 298 | break; | ||
| 299 | } | ||
| 300 | case Maxwell::ShaderProgram::Geometry: { | ||
| 301 | shader_program_manager->UseProgrammableGeometryShader( | ||
| 302 | shader->GetProgramHandle(primitive_mode)); | ||
| 292 | break; | 303 | break; |
| 293 | } | 304 | } |
| 294 | case Maxwell::ShaderProgram::Fragment: { | 305 | case Maxwell::ShaderProgram::Fragment: { |
| 295 | shader_program_manager->UseProgrammableFragmentShader(shader->GetProgramHandle()); | 306 | shader_program_manager->UseProgrammableFragmentShader( |
| 307 | shader->GetProgramHandle(primitive_mode)); | ||
| 296 | break; | 308 | break; |
| 297 | } | 309 | } |
| 298 | default: | 310 | default: |
| @@ -302,12 +314,13 @@ void RasterizerOpenGL::SetupShaders() { | |||
| 302 | } | 314 | } |
| 303 | 315 | ||
| 304 | // Configure the const buffers for this shader stage. | 316 | // Configure the const buffers for this shader stage. |
| 305 | current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), | 317 | current_constbuffer_bindpoint = |
| 306 | shader, current_constbuffer_bindpoint); | 318 | SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), shader, primitive_mode, |
| 319 | current_constbuffer_bindpoint); | ||
| 307 | 320 | ||
| 308 | // Configure the textures for this shader stage. | 321 | // Configure the textures for this shader stage. |
| 309 | current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader, | 322 | current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader, |
| 310 | current_texture_bindpoint); | 323 | primitive_mode, current_texture_bindpoint); |
| 311 | 324 | ||
| 312 | // When VertexA is enabled, we have dual vertex shaders | 325 | // When VertexA is enabled, we have dual vertex shaders |
| 313 | if (program == Maxwell::ShaderProgram::VertexA) { | 326 | if (program == Maxwell::ShaderProgram::VertexA) { |
| @@ -317,8 +330,6 @@ void RasterizerOpenGL::SetupShaders() { | |||
| 317 | } | 330 | } |
| 318 | 331 | ||
| 319 | state.Apply(); | 332 | state.Apply(); |
| 320 | |||
| 321 | shader_program_manager->UseTrivialGeometryShader(); | ||
| 322 | } | 333 | } |
| 323 | 334 | ||
| 324 | std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { | 335 | std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { |
| @@ -541,6 +552,7 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 541 | SyncLogicOpState(); | 552 | SyncLogicOpState(); |
| 542 | SyncCullMode(); | 553 | SyncCullMode(); |
| 543 | SyncAlphaTest(); | 554 | SyncAlphaTest(); |
| 555 | SyncScissorTest(); | ||
| 544 | SyncTransformFeedback(); | 556 | SyncTransformFeedback(); |
| 545 | SyncPointState(); | 557 | SyncPointState(); |
| 546 | 558 | ||
| @@ -580,7 +592,7 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 580 | 592 | ||
| 581 | SetupVertexArrays(); | 593 | SetupVertexArrays(); |
| 582 | DrawParameters params = SetupDraw(); | 594 | DrawParameters params = SetupDraw(); |
| 583 | SetupShaders(); | 595 | SetupShaders(params.primitive_mode); |
| 584 | 596 | ||
| 585 | buffer_cache.Unmap(); | 597 | buffer_cache.Unmap(); |
| 586 | 598 | ||
| @@ -719,7 +731,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr | |||
| 719 | } | 731 | } |
| 720 | 732 | ||
| 721 | u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, | 733 | u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, |
| 722 | u32 current_bindpoint) { | 734 | GLenum primitive_mode, u32 current_bindpoint) { |
| 723 | MICROPROFILE_SCOPE(OpenGL_UBO); | 735 | MICROPROFILE_SCOPE(OpenGL_UBO); |
| 724 | const auto& gpu = Core::System::GetInstance().GPU(); | 736 | const auto& gpu = Core::System::GetInstance().GPU(); |
| 725 | const auto& maxwell3d = gpu.Maxwell3D(); | 737 | const auto& maxwell3d = gpu.Maxwell3D(); |
| @@ -771,7 +783,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad | |||
| 771 | buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); | 783 | buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); |
| 772 | 784 | ||
| 773 | // Now configure the bindpoint of the buffer inside the shader | 785 | // Now configure the bindpoint of the buffer inside the shader |
| 774 | glUniformBlockBinding(shader->GetProgramHandle(), | 786 | glUniformBlockBinding(shader->GetProgramHandle(primitive_mode), |
| 775 | shader->GetProgramResourceIndex(used_buffer), | 787 | shader->GetProgramResourceIndex(used_buffer), |
| 776 | current_bindpoint + bindpoint); | 788 | current_bindpoint + bindpoint); |
| 777 | 789 | ||
| @@ -787,7 +799,8 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad | |||
| 787 | return current_bindpoint + static_cast<u32>(entries.size()); | 799 | return current_bindpoint + static_cast<u32>(entries.size()); |
| 788 | } | 800 | } |
| 789 | 801 | ||
| 790 | u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) { | 802 | u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, |
| 803 | GLenum primitive_mode, u32 current_unit) { | ||
| 791 | MICROPROFILE_SCOPE(OpenGL_Texture); | 804 | MICROPROFILE_SCOPE(OpenGL_Texture); |
| 792 | const auto& gpu = Core::System::GetInstance().GPU(); | 805 | const auto& gpu = Core::System::GetInstance().GPU(); |
| 793 | const auto& maxwell3d = gpu.Maxwell3D(); | 806 | const auto& maxwell3d = gpu.Maxwell3D(); |
| @@ -802,8 +815,8 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, | |||
| 802 | 815 | ||
| 803 | // Bind the uniform to the sampler. | 816 | // Bind the uniform to the sampler. |
| 804 | 817 | ||
| 805 | glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry), | 818 | glProgramUniform1i(shader->GetProgramHandle(primitive_mode), |
| 806 | current_bindpoint); | 819 | shader->GetUniformLocation(entry), current_bindpoint); |
| 807 | 820 | ||
| 808 | const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); | 821 | const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); |
| 809 | 822 | ||
| @@ -972,6 +985,22 @@ void RasterizerOpenGL::SyncAlphaTest() { | |||
| 972 | } | 985 | } |
| 973 | } | 986 | } |
| 974 | 987 | ||
| 988 | void RasterizerOpenGL::SyncScissorTest() { | ||
| 989 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||
| 990 | |||
| 991 | state.scissor.enabled = (regs.scissor_test.enable != 0); | ||
| 992 | // TODO(Blinkhawk): Figure if the hardware supports scissor testing per viewport and how it's | ||
| 993 | // implemented. | ||
| 994 | if (regs.scissor_test.enable != 0) { | ||
| 995 | const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x; | ||
| 996 | const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y; | ||
| 997 | state.scissor.x = regs.scissor_test.min_x; | ||
| 998 | state.scissor.y = regs.scissor_test.min_y; | ||
| 999 | state.scissor.width = width; | ||
| 1000 | state.scissor.height = height; | ||
| 1001 | } | ||
| 1002 | } | ||
| 1003 | |||
| 975 | void RasterizerOpenGL::SyncTransformFeedback() { | 1004 | void RasterizerOpenGL::SyncTransformFeedback() { |
| 976 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 1005 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 977 | 1006 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 0dab2018b..b1f7ccc7e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -120,7 +120,7 @@ private: | |||
| 120 | * @returns The next available bindpoint for use in the next shader stage. | 120 | * @returns The next available bindpoint for use in the next shader stage. |
| 121 | */ | 121 | */ |
| 122 | u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, | 122 | u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, |
| 123 | u32 current_bindpoint); | 123 | GLenum primitive_mode, u32 current_bindpoint); |
| 124 | 124 | ||
| 125 | /* | 125 | /* |
| 126 | * Configures the current textures to use for the draw command. | 126 | * Configures the current textures to use for the draw command. |
| @@ -130,7 +130,7 @@ private: | |||
| 130 | * @returns The next available bindpoint for use in the next shader stage. | 130 | * @returns The next available bindpoint for use in the next shader stage. |
| 131 | */ | 131 | */ |
| 132 | u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, | 132 | u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, |
| 133 | u32 current_unit); | 133 | GLenum primitive_mode, u32 current_unit); |
| 134 | 134 | ||
| 135 | /// Syncs the viewport to match the guest state | 135 | /// Syncs the viewport to match the guest state |
| 136 | void SyncViewport(); | 136 | void SyncViewport(); |
| @@ -165,6 +165,9 @@ private: | |||
| 165 | /// Syncs the alpha test state to match the guest state | 165 | /// Syncs the alpha test state to match the guest state |
| 166 | void SyncAlphaTest(); | 166 | void SyncAlphaTest(); |
| 167 | 167 | ||
| 168 | /// Syncs the scissor test state to match the guest state | ||
| 169 | void SyncScissorTest(); | ||
| 170 | |||
| 168 | /// Syncs the transform feedback state to match the guest state | 171 | /// Syncs the transform feedback state to match the guest state |
| 169 | void SyncTransformFeedback(); | 172 | void SyncTransformFeedback(); |
| 170 | 173 | ||
| @@ -207,7 +210,7 @@ private: | |||
| 207 | 210 | ||
| 208 | DrawParameters SetupDraw(); | 211 | DrawParameters SetupDraw(); |
| 209 | 212 | ||
| 210 | void SetupShaders(); | 213 | void SetupShaders(GLenum primitive_mode); |
| 211 | 214 | ||
| 212 | enum class AccelDraw { Disabled, Arrays, Indexed }; | 215 | enum class AccelDraw { Disabled, Arrays, Indexed }; |
| 213 | AccelDraw accelerate_draw = AccelDraw::Disabled; | 216 | 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 56ff83eff..65a220c41 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -45,7 +45,9 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | |||
| 45 | SurfaceParams params{}; | 45 | SurfaceParams params{}; |
| 46 | params.addr = TryGetCpuAddr(config.tic.Address()); | 46 | params.addr = TryGetCpuAddr(config.tic.Address()); |
| 47 | params.is_tiled = config.tic.IsTiled(); | 47 | params.is_tiled = config.tic.IsTiled(); |
| 48 | params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, | ||
| 48 | params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, | 49 | params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, |
| 50 | params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0, | ||
| 49 | params.pixel_format = | 51 | params.pixel_format = |
| 50 | PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value()); | 52 | PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value()); |
| 51 | params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); | 53 | params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); |
| @@ -97,8 +99,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | |||
| 97 | const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; | 99 | const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; |
| 98 | SurfaceParams params{}; | 100 | SurfaceParams params{}; |
| 99 | params.addr = TryGetCpuAddr(config.Address()); | 101 | params.addr = TryGetCpuAddr(config.Address()); |
| 100 | params.is_tiled = true; | 102 | params.is_tiled = |
| 101 | params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; | 103 | config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; |
| 104 | params.block_width = 1 << config.memory_layout.block_width; | ||
| 105 | params.block_height = 1 << config.memory_layout.block_height; | ||
| 106 | params.block_depth = 1 << config.memory_layout.block_depth; | ||
| 102 | params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); | 107 | params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); |
| 103 | params.component_type = ComponentTypeFromRenderTarget(config.format); | 108 | params.component_type = ComponentTypeFromRenderTarget(config.format); |
| 104 | params.type = GetFormatType(params.pixel_format); | 109 | params.type = GetFormatType(params.pixel_format); |
| @@ -120,13 +125,16 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | |||
| 120 | return params; | 125 | return params; |
| 121 | } | 126 | } |
| 122 | 127 | ||
| 123 | /*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, | 128 | /*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer( |
| 124 | Tegra::GPUVAddr zeta_address, | 129 | u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format, |
| 125 | Tegra::DepthFormat format) { | 130 | u32 block_width, u32 block_height, u32 block_depth, |
| 131 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { | ||
| 126 | SurfaceParams params{}; | 132 | SurfaceParams params{}; |
| 127 | params.addr = TryGetCpuAddr(zeta_address); | 133 | params.addr = TryGetCpuAddr(zeta_address); |
| 128 | params.is_tiled = true; | 134 | params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; |
| 129 | params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; | 135 | params.block_width = 1 << std::min(block_width, 5U); |
| 136 | params.block_height = 1 << std::min(block_height, 5U); | ||
| 137 | params.block_depth = 1 << std::min(block_depth, 5U); | ||
| 130 | params.pixel_format = PixelFormatFromDepthFormat(format); | 138 | params.pixel_format = PixelFormatFromDepthFormat(format); |
| 131 | params.component_type = ComponentTypeFromDepthFormat(format); | 139 | params.component_type = ComponentTypeFromDepthFormat(format); |
| 132 | params.type = GetFormatType(params.pixel_format); | 140 | params.type = GetFormatType(params.pixel_format); |
| @@ -148,7 +156,9 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | |||
| 148 | SurfaceParams params{}; | 156 | SurfaceParams params{}; |
| 149 | params.addr = TryGetCpuAddr(config.Address()); | 157 | params.addr = TryGetCpuAddr(config.Address()); |
| 150 | params.is_tiled = !config.linear; | 158 | params.is_tiled = !config.linear; |
| 151 | params.block_height = params.is_tiled ? config.BlockHeight() : 0, | 159 | params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0, |
| 160 | params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, | ||
| 161 | params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0, | ||
| 152 | params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); | 162 | params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); |
| 153 | params.component_type = ComponentTypeFromRenderTarget(config.format); | 163 | params.component_type = ComponentTypeFromRenderTarget(config.format); |
| 154 | params.type = GetFormatType(params.pixel_format); | 164 | params.type = GetFormatType(params.pixel_format); |
| @@ -818,6 +828,11 @@ void CachedSurface::LoadGLBuffer() { | |||
| 818 | if (params.is_tiled) { | 828 | if (params.is_tiled) { |
| 819 | gl_buffer.resize(total_size); | 829 | gl_buffer.resize(total_size); |
| 820 | 830 | ||
| 831 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", | ||
| 832 | params.block_width, static_cast<u32>(params.target)); | ||
| 833 | ASSERT_MSG(params.block_depth == 1, "Block depth is defined as {} on texture type {}", | ||
| 834 | params.block_depth, static_cast<u32>(params.target)); | ||
| 835 | |||
| 821 | // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do | 836 | // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do |
| 822 | // this for 3D textures, etc. | 837 | // this for 3D textures, etc. |
| 823 | switch (params.target) { | 838 | switch (params.target) { |
| @@ -989,7 +1004,9 @@ Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) { | |||
| 989 | } | 1004 | } |
| 990 | 1005 | ||
| 991 | SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer( | 1006 | SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer( |
| 992 | regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format)}; | 1007 | regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format, |
| 1008 | regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height, | ||
| 1009 | regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)}; | ||
| 993 | 1010 | ||
| 994 | return GetSurface(depth_params, preserve_contents); | 1011 | return GetSurface(depth_params, preserve_contents); |
| 995 | } | 1012 | } |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 0b4940b3c..66d98ad4e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -716,9 +716,10 @@ struct SurfaceParams { | |||
| 716 | static SurfaceParams CreateForFramebuffer(std::size_t index); | 716 | static SurfaceParams CreateForFramebuffer(std::size_t index); |
| 717 | 717 | ||
| 718 | /// Creates SurfaceParams for a depth buffer configuration | 718 | /// Creates SurfaceParams for a depth buffer configuration |
| 719 | static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, | 719 | static SurfaceParams CreateForDepthBuffer( |
| 720 | Tegra::GPUVAddr zeta_address, | 720 | u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format, |
| 721 | Tegra::DepthFormat format); | 721 | u32 block_width, u32 block_height, u32 block_depth, |
| 722 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); | ||
| 722 | 723 | ||
| 723 | /// Creates SurfaceParams for a Fermi2D surface copy | 724 | /// Creates SurfaceParams for a Fermi2D surface copy |
| 724 | static SurfaceParams CreateForFermiCopySurface( | 725 | static SurfaceParams CreateForFermiCopySurface( |
| @@ -733,7 +734,9 @@ struct SurfaceParams { | |||
| 733 | 734 | ||
| 734 | VAddr addr; | 735 | VAddr addr; |
| 735 | bool is_tiled; | 736 | bool is_tiled; |
| 737 | u32 block_width; | ||
| 736 | u32 block_height; | 738 | u32 block_height; |
| 739 | u32 block_depth; | ||
| 737 | PixelFormat pixel_format; | 740 | PixelFormat pixel_format; |
| 738 | ComponentType component_type; | 741 | ComponentType component_type; |
| 739 | SurfaceType type; | 742 | SurfaceType type; |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 7cd8f91e4..1a03a677f 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -68,6 +68,10 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) | |||
| 68 | program_result = GLShader::GenerateVertexShader(setup); | 68 | program_result = GLShader::GenerateVertexShader(setup); |
| 69 | gl_type = GL_VERTEX_SHADER; | 69 | gl_type = GL_VERTEX_SHADER; |
| 70 | break; | 70 | break; |
| 71 | case Maxwell::ShaderProgram::Geometry: | ||
| 72 | program_result = GLShader::GenerateGeometryShader(setup); | ||
| 73 | gl_type = GL_GEOMETRY_SHADER; | ||
| 74 | break; | ||
| 71 | case Maxwell::ShaderProgram::Fragment: | 75 | case Maxwell::ShaderProgram::Fragment: |
| 72 | program_result = GLShader::GenerateFragmentShader(setup); | 76 | program_result = GLShader::GenerateFragmentShader(setup); |
| 73 | gl_type = GL_FRAGMENT_SHADER; | 77 | gl_type = GL_FRAGMENT_SHADER; |
| @@ -80,11 +84,16 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) | |||
| 80 | 84 | ||
| 81 | entries = program_result.second; | 85 | entries = program_result.second; |
| 82 | 86 | ||
| 83 | OGLShader shader; | 87 | if (program_type != Maxwell::ShaderProgram::Geometry) { |
| 84 | shader.Create(program_result.first.c_str(), gl_type); | 88 | OGLShader shader; |
| 85 | program.Create(true, shader.handle); | 89 | shader.Create(program_result.first.c_str(), gl_type); |
| 86 | SetShaderUniformBlockBindings(program.handle); | 90 | program.Create(true, shader.handle); |
| 87 | VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr); | 91 | SetShaderUniformBlockBindings(program.handle); |
| 92 | VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr); | ||
| 93 | } else { | ||
| 94 | // Store shader's code to lazily build it on draw | ||
| 95 | geometry_programs.code = program_result.first; | ||
| 96 | } | ||
| 88 | } | 97 | } |
| 89 | 98 | ||
| 90 | GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) { | 99 | GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) { |
| @@ -110,6 +119,21 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) { | |||
| 110 | return search->second; | 119 | return search->second; |
| 111 | } | 120 | } |
| 112 | 121 | ||
| 122 | GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, | ||
| 123 | const std::string& glsl_topology, | ||
| 124 | const std::string& debug_name) { | ||
| 125 | if (target_program.handle != 0) { | ||
| 126 | return target_program.handle; | ||
| 127 | } | ||
| 128 | const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"}; | ||
| 129 | OGLShader shader; | ||
| 130 | shader.Create(source.c_str(), GL_GEOMETRY_SHADER); | ||
| 131 | target_program.Create(true, shader.handle); | ||
| 132 | SetShaderUniformBlockBindings(target_program.handle); | ||
| 133 | VideoCore::LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name); | ||
| 134 | return target_program.handle; | ||
| 135 | }; | ||
| 136 | |||
| 113 | Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | 137 | Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { |
| 114 | const VAddr program_addr{GetShaderAddress(program)}; | 138 | const VAddr program_addr{GetShaderAddress(program)}; |
| 115 | 139 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 9bafe43a9..7bb287f56 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <map> | 7 | #include <map> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | 9 | ||
| 10 | #include "common/assert.h" | ||
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 11 | #include "video_core/rasterizer_cache.h" | 12 | #include "video_core/rasterizer_cache.h" |
| 12 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 13 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| @@ -38,8 +39,31 @@ public: | |||
| 38 | } | 39 | } |
| 39 | 40 | ||
| 40 | /// Gets the GL program handle for the shader | 41 | /// Gets the GL program handle for the shader |
| 41 | GLuint GetProgramHandle() const { | 42 | GLuint GetProgramHandle(GLenum primitive_mode) { |
| 42 | return program.handle; | 43 | if (program_type != Maxwell::ShaderProgram::Geometry) { |
| 44 | return program.handle; | ||
| 45 | } | ||
| 46 | switch (primitive_mode) { | ||
| 47 | case GL_POINTS: | ||
| 48 | return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints"); | ||
| 49 | case GL_LINES: | ||
| 50 | case GL_LINE_STRIP: | ||
| 51 | return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines"); | ||
| 52 | case GL_LINES_ADJACENCY: | ||
| 53 | case GL_LINE_STRIP_ADJACENCY: | ||
| 54 | return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency", | ||
| 55 | "ShaderLinesAdjacency"); | ||
| 56 | case GL_TRIANGLES: | ||
| 57 | case GL_TRIANGLE_STRIP: | ||
| 58 | case GL_TRIANGLE_FAN: | ||
| 59 | return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles"); | ||
| 60 | case GL_TRIANGLES_ADJACENCY: | ||
| 61 | case GL_TRIANGLE_STRIP_ADJACENCY: | ||
| 62 | return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency", | ||
| 63 | "ShaderLines"); | ||
| 64 | default: | ||
| 65 | UNREACHABLE_MSG("Unknown primitive mode."); | ||
| 66 | } | ||
| 43 | } | 67 | } |
| 44 | 68 | ||
| 45 | /// Gets the GL program resource location for the specified resource, caching as needed | 69 | /// Gets the GL program resource location for the specified resource, caching as needed |
| @@ -49,12 +73,30 @@ public: | |||
| 49 | GLint GetUniformLocation(const GLShader::SamplerEntry& sampler); | 73 | GLint GetUniformLocation(const GLShader::SamplerEntry& sampler); |
| 50 | 74 | ||
| 51 | private: | 75 | private: |
| 76 | /// Generates a geometry shader or returns one that already exists. | ||
| 77 | GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology, | ||
| 78 | const std::string& debug_name); | ||
| 79 | |||
| 52 | VAddr addr; | 80 | VAddr addr; |
| 53 | Maxwell::ShaderProgram program_type; | 81 | Maxwell::ShaderProgram program_type; |
| 54 | GLShader::ShaderSetup setup; | 82 | GLShader::ShaderSetup setup; |
| 55 | GLShader::ShaderEntries entries; | 83 | GLShader::ShaderEntries entries; |
| 84 | |||
| 85 | // Non-geometry program. | ||
| 56 | OGLProgram program; | 86 | OGLProgram program; |
| 57 | 87 | ||
| 88 | // Geometry programs. These are needed because GLSL needs an input topology but it's not | ||
| 89 | // declared by the hardware. Workaround this issue by generating a different shader per input | ||
| 90 | // topology class. | ||
| 91 | struct { | ||
| 92 | std::string code; | ||
| 93 | OGLProgram points; | ||
| 94 | OGLProgram lines; | ||
| 95 | OGLProgram lines_adjacency; | ||
| 96 | OGLProgram triangles; | ||
| 97 | OGLProgram triangles_adjacency; | ||
| 98 | } geometry_programs; | ||
| 99 | |||
| 58 | std::map<u32, GLuint> resource_cache; | 100 | std::map<u32, GLuint> resource_cache; |
| 59 | std::map<u32, GLint> uniform_cache; | 101 | std::map<u32, GLint> uniform_cache; |
| 60 | }; | 102 | }; |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 7e57de78a..8dfb49507 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <string_view> | 8 | #include <string_view> |
| 9 | 9 | ||
| 10 | #include <boost/optional.hpp> | ||
| 10 | #include <fmt/format.h> | 11 | #include <fmt/format.h> |
| 11 | 12 | ||
| 12 | #include "common/assert.h" | 13 | #include "common/assert.h" |
| @@ -29,11 +30,32 @@ using Tegra::Shader::SubOp; | |||
| 29 | constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; | 30 | constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; |
| 30 | constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); | 31 | constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); |
| 31 | 32 | ||
| 33 | enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 }; | ||
| 34 | |||
| 35 | constexpr u32 MAX_GEOMETRY_BUFFERS = 6; | ||
| 36 | constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested | ||
| 37 | |||
| 32 | class DecompileFail : public std::runtime_error { | 38 | class DecompileFail : public std::runtime_error { |
| 33 | public: | 39 | public: |
| 34 | using std::runtime_error::runtime_error; | 40 | using std::runtime_error::runtime_error; |
| 35 | }; | 41 | }; |
| 36 | 42 | ||
| 43 | /// Translate topology | ||
| 44 | static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { | ||
| 45 | switch (topology) { | ||
| 46 | case Tegra::Shader::OutputTopology::PointList: | ||
| 47 | return "points"; | ||
| 48 | case Tegra::Shader::OutputTopology::LineStrip: | ||
| 49 | return "line_strip"; | ||
| 50 | case Tegra::Shader::OutputTopology::TriangleStrip: | ||
| 51 | return "triangle_strip"; | ||
| 52 | default: | ||
| 53 | LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology)); | ||
| 54 | UNREACHABLE(); | ||
| 55 | return "points"; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 37 | /// Describes the behaviour of code path of a given entry point and a return point. | 59 | /// Describes the behaviour of code path of a given entry point and a return point. |
| 38 | enum class ExitMethod { | 60 | enum class ExitMethod { |
| 39 | Undetermined, ///< Internal value. Only occur when analyzing JMP loop. | 61 | Undetermined, ///< Internal value. Only occur when analyzing JMP loop. |
| @@ -253,8 +275,9 @@ enum class InternalFlag : u64 { | |||
| 253 | class GLSLRegisterManager { | 275 | class GLSLRegisterManager { |
| 254 | public: | 276 | public: |
| 255 | GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, | 277 | GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, |
| 256 | const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix) | 278 | const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, |
| 257 | : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} { | 279 | const Tegra::Shader::Header& header) |
| 280 | : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} { | ||
| 258 | BuildRegisterList(); | 281 | BuildRegisterList(); |
| 259 | BuildInputList(); | 282 | BuildInputList(); |
| 260 | } | 283 | } |
| @@ -358,11 +381,13 @@ public: | |||
| 358 | * @param reg The destination register to use. | 381 | * @param reg The destination register to use. |
| 359 | * @param elem The element to use for the operation. | 382 | * @param elem The element to use for the operation. |
| 360 | * @param attribute The input attribute to use as the source value. | 383 | * @param attribute The input attribute to use as the source value. |
| 384 | * @param vertex The register that decides which vertex to read from (used in GS). | ||
| 361 | */ | 385 | */ |
| 362 | void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, | 386 | void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, |
| 363 | const Tegra::Shader::IpaMode& input_mode) { | 387 | const Tegra::Shader::IpaMode& input_mode, |
| 388 | boost::optional<Register> vertex = {}) { | ||
| 364 | const std::string dest = GetRegisterAsFloat(reg); | 389 | const std::string dest = GetRegisterAsFloat(reg); |
| 365 | const std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem); | 390 | const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem); |
| 366 | shader.AddLine(dest + " = " + src + ';'); | 391 | shader.AddLine(dest + " = " + src + ';'); |
| 367 | } | 392 | } |
| 368 | 393 | ||
| @@ -391,16 +416,29 @@ public: | |||
| 391 | * are stored as floats, so this may require conversion. | 416 | * are stored as floats, so this may require conversion. |
| 392 | * @param attribute The destination output attribute. | 417 | * @param attribute The destination output attribute. |
| 393 | * @param elem The element to use for the operation. | 418 | * @param elem The element to use for the operation. |
| 394 | * @param reg The register to use as the source value. | 419 | * @param val_reg The register to use as the source value. |
| 420 | * @param buf_reg The register that tells which buffer to write to (used in geometry shaders). | ||
| 395 | */ | 421 | */ |
| 396 | void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { | 422 | void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& val_reg, |
| 423 | const Register& buf_reg) { | ||
| 397 | const std::string dest = GetOutputAttribute(attribute); | 424 | const std::string dest = GetOutputAttribute(attribute); |
| 398 | const std::string src = GetRegisterAsFloat(reg); | 425 | const std::string src = GetRegisterAsFloat(val_reg); |
| 399 | 426 | ||
| 400 | if (!dest.empty()) { | 427 | if (!dest.empty()) { |
| 401 | // Can happen with unknown/unimplemented output attributes, in which case we ignore the | 428 | // Can happen with unknown/unimplemented output attributes, in which case we ignore the |
| 402 | // instruction for now. | 429 | // instruction for now. |
| 403 | shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); | 430 | if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { |
| 431 | // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry | ||
| 432 | // shader. These instructions use a dirty register as buffer index. To avoid some | ||
| 433 | // drivers from complaining for the out of boundary writes, guard them. | ||
| 434 | const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " + | ||
| 435 | std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'}; | ||
| 436 | shader.AddLine("amem[" + buf_index + "][" + | ||
| 437 | std::to_string(static_cast<u32>(attribute)) + ']' + | ||
| 438 | GetSwizzle(elem) + " = " + src + ';'); | ||
| 439 | } else { | ||
| 440 | shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); | ||
| 441 | } | ||
| 404 | } | 442 | } |
| 405 | } | 443 | } |
| 406 | 444 | ||
| @@ -441,41 +479,123 @@ public: | |||
| 441 | } | 479 | } |
| 442 | } | 480 | } |
| 443 | 481 | ||
| 444 | /// Add declarations for registers | 482 | /// Add declarations. |
| 445 | void GenerateDeclarations(const std::string& suffix) { | 483 | void GenerateDeclarations(const std::string& suffix) { |
| 484 | GenerateRegisters(suffix); | ||
| 485 | GenerateInternalFlags(); | ||
| 486 | GenerateInputAttrs(); | ||
| 487 | GenerateOutputAttrs(); | ||
| 488 | GenerateConstBuffers(); | ||
| 489 | GenerateSamplers(); | ||
| 490 | GenerateGeometry(); | ||
| 491 | } | ||
| 492 | |||
| 493 | /// Returns a list of constant buffer declarations. | ||
| 494 | std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const { | ||
| 495 | std::vector<ConstBufferEntry> result; | ||
| 496 | std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(), | ||
| 497 | std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); }); | ||
| 498 | return result; | ||
| 499 | } | ||
| 500 | |||
| 501 | /// Returns a list of samplers used in the shader. | ||
| 502 | const std::vector<SamplerEntry>& GetSamplers() const { | ||
| 503 | return used_samplers; | ||
| 504 | } | ||
| 505 | |||
| 506 | /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if | ||
| 507 | /// necessary. | ||
| 508 | std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, | ||
| 509 | bool is_array, bool is_shadow) { | ||
| 510 | const auto offset = static_cast<std::size_t>(sampler.index.Value()); | ||
| 511 | |||
| 512 | // If this sampler has already been used, return the existing mapping. | ||
| 513 | const auto itr = | ||
| 514 | std::find_if(used_samplers.begin(), used_samplers.end(), | ||
| 515 | [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); | ||
| 516 | |||
| 517 | if (itr != used_samplers.end()) { | ||
| 518 | ASSERT(itr->GetType() == type && itr->IsArray() == is_array && | ||
| 519 | itr->IsShadow() == is_shadow); | ||
| 520 | return itr->GetName(); | ||
| 521 | } | ||
| 522 | |||
| 523 | // Otherwise create a new mapping for this sampler | ||
| 524 | const std::size_t next_index = used_samplers.size(); | ||
| 525 | const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow}; | ||
| 526 | used_samplers.emplace_back(entry); | ||
| 527 | return entry.GetName(); | ||
| 528 | } | ||
| 529 | |||
| 530 | private: | ||
| 531 | /// Generates declarations for registers. | ||
| 532 | void GenerateRegisters(const std::string& suffix) { | ||
| 446 | for (const auto& reg : regs) { | 533 | for (const auto& reg : regs) { |
| 447 | declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() + | 534 | declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() + |
| 448 | std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;"); | 535 | std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;"); |
| 449 | } | 536 | } |
| 450 | declarations.AddNewLine(); | 537 | declarations.AddNewLine(); |
| 538 | } | ||
| 451 | 539 | ||
| 540 | /// Generates declarations for internal flags. | ||
| 541 | void GenerateInternalFlags() { | ||
| 452 | for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { | 542 | for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { |
| 453 | const InternalFlag code = static_cast<InternalFlag>(ii); | 543 | const InternalFlag code = static_cast<InternalFlag>(ii); |
| 454 | declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); | 544 | declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); |
| 455 | } | 545 | } |
| 456 | declarations.AddNewLine(); | 546 | declarations.AddNewLine(); |
| 547 | } | ||
| 548 | |||
| 549 | /// Generates declarations for input attributes. | ||
| 550 | void GenerateInputAttrs() { | ||
| 551 | if (stage != Maxwell3D::Regs::ShaderStage::Vertex) { | ||
| 552 | const std::string attr = | ||
| 553 | stage == Maxwell3D::Regs::ShaderStage::Geometry ? "gs_position[]" : "position"; | ||
| 554 | declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) + | ||
| 555 | ") in vec4 " + attr + ';'); | ||
| 556 | } | ||
| 457 | 557 | ||
| 458 | for (const auto element : declr_input_attribute) { | 558 | for (const auto element : declr_input_attribute) { |
| 459 | // TODO(bunnei): Use proper number of elements for these | 559 | // TODO(bunnei): Use proper number of elements for these |
| 460 | u32 idx = | 560 | u32 idx = |
| 461 | static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0); | 561 | static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0); |
| 462 | declarations.AddLine("layout(location = " + std::to_string(idx) + ")" + | 562 | if (stage != Maxwell3D::Regs::ShaderStage::Vertex) { |
| 463 | GetInputFlags(element.first) + "in vec4 " + | 563 | // If inputs are varyings, add an offset |
| 464 | GetInputAttribute(element.first, element.second) + ';'); | 564 | idx += GENERIC_VARYING_START_LOCATION; |
| 565 | } | ||
| 566 | |||
| 567 | std::string attr{GetInputAttribute(element.first, element.second)}; | ||
| 568 | if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { | ||
| 569 | attr = "gs_" + attr + "[]"; | ||
| 570 | } | ||
| 571 | declarations.AddLine("layout (location = " + std::to_string(idx) + ") " + | ||
| 572 | GetInputFlags(element.first) + "in vec4 " + attr + ';'); | ||
| 465 | } | 573 | } |
| 574 | |||
| 466 | declarations.AddNewLine(); | 575 | declarations.AddNewLine(); |
| 576 | } | ||
| 467 | 577 | ||
| 578 | /// Generates declarations for output attributes. | ||
| 579 | void GenerateOutputAttrs() { | ||
| 580 | if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { | ||
| 581 | declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) + | ||
| 582 | ") out vec4 position;"); | ||
| 583 | } | ||
| 468 | for (const auto& index : declr_output_attribute) { | 584 | for (const auto& index : declr_output_attribute) { |
| 469 | // TODO(bunnei): Use proper number of elements for these | 585 | // TODO(bunnei): Use proper number of elements for these |
| 470 | declarations.AddLine("layout(location = " + | 586 | const u32 idx = static_cast<u32>(index) - |
| 471 | std::to_string(static_cast<u32>(index) - | 587 | static_cast<u32>(Attribute::Index::Attribute_0) + |
| 472 | static_cast<u32>(Attribute::Index::Attribute_0)) + | 588 | GENERIC_VARYING_START_LOCATION; |
| 473 | ") out vec4 " + GetOutputAttribute(index) + ';'); | 589 | declarations.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " + |
| 590 | GetOutputAttribute(index) + ';'); | ||
| 474 | } | 591 | } |
| 475 | declarations.AddNewLine(); | 592 | declarations.AddNewLine(); |
| 593 | } | ||
| 476 | 594 | ||
| 595 | /// Generates declarations for constant buffers. | ||
| 596 | void GenerateConstBuffers() { | ||
| 477 | for (const auto& entry : GetConstBuffersDeclarations()) { | 597 | for (const auto& entry : GetConstBuffersDeclarations()) { |
| 478 | declarations.AddLine("layout(std140) uniform " + entry.GetName()); | 598 | declarations.AddLine("layout (std140) uniform " + entry.GetName()); |
| 479 | declarations.AddLine('{'); | 599 | declarations.AddLine('{'); |
| 480 | declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) + | 600 | declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) + |
| 481 | "[MAX_CONSTBUFFER_ELEMENTS];"); | 601 | "[MAX_CONSTBUFFER_ELEMENTS];"); |
| @@ -483,7 +603,10 @@ public: | |||
| 483 | declarations.AddNewLine(); | 603 | declarations.AddNewLine(); |
| 484 | } | 604 | } |
| 485 | declarations.AddNewLine(); | 605 | declarations.AddNewLine(); |
| 606 | } | ||
| 486 | 607 | ||
| 608 | /// Generates declarations for samplers. | ||
| 609 | void GenerateSamplers() { | ||
| 487 | const auto& samplers = GetSamplers(); | 610 | const auto& samplers = GetSamplers(); |
| 488 | for (const auto& sampler : samplers) { | 611 | for (const auto& sampler : samplers) { |
| 489 | declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() + | 612 | declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() + |
| @@ -492,44 +615,42 @@ public: | |||
| 492 | declarations.AddNewLine(); | 615 | declarations.AddNewLine(); |
| 493 | } | 616 | } |
| 494 | 617 | ||
| 495 | /// Returns a list of constant buffer declarations | 618 | /// Generates declarations used for geometry shaders. |
| 496 | std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const { | 619 | void GenerateGeometry() { |
| 497 | std::vector<ConstBufferEntry> result; | 620 | if (stage != Maxwell3D::Regs::ShaderStage::Geometry) |
| 498 | std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(), | 621 | return; |
| 499 | std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); }); | ||
| 500 | return result; | ||
| 501 | } | ||
| 502 | |||
| 503 | /// Returns a list of samplers used in the shader | ||
| 504 | const std::vector<SamplerEntry>& GetSamplers() const { | ||
| 505 | return used_samplers; | ||
| 506 | } | ||
| 507 | |||
| 508 | /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if | ||
| 509 | /// necessary. | ||
| 510 | std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, | ||
| 511 | bool is_array, bool is_shadow) { | ||
| 512 | const std::size_t offset = static_cast<std::size_t>(sampler.index.Value()); | ||
| 513 | 622 | ||
| 514 | // If this sampler has already been used, return the existing mapping. | 623 | declarations.AddLine( |
| 515 | const auto itr = | 624 | "layout (" + GetTopologyName(header.common3.output_topology) + |
| 516 | std::find_if(used_samplers.begin(), used_samplers.end(), | 625 | ", max_vertices = " + std::to_string(header.common4.max_output_vertices) + ") out;"); |
| 517 | [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); | 626 | declarations.AddNewLine(); |
| 518 | 627 | ||
| 519 | if (itr != used_samplers.end()) { | 628 | declarations.AddLine("vec4 amem[" + std::to_string(MAX_GEOMETRY_BUFFERS) + "][" + |
| 520 | ASSERT(itr->GetType() == type && itr->IsArray() == is_array && | 629 | std::to_string(MAX_ATTRIBUTES) + "];"); |
| 521 | itr->IsShadow() == is_shadow); | 630 | declarations.AddNewLine(); |
| 522 | return itr->GetName(); | ||
| 523 | } | ||
| 524 | 631 | ||
| 525 | // Otherwise create a new mapping for this sampler | 632 | constexpr char buffer[] = "amem[output_buffer]"; |
| 526 | const std::size_t next_index = used_samplers.size(); | 633 | declarations.AddLine("void emit_vertex(uint output_buffer) {"); |
| 527 | const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow}; | 634 | ++declarations.scope; |
| 528 | used_samplers.emplace_back(entry); | 635 | for (const auto element : declr_output_attribute) { |
| 529 | return entry.GetName(); | 636 | declarations.AddLine(GetOutputAttribute(element) + " = " + buffer + '[' + |
| 637 | std::to_string(static_cast<u32>(element)) + "];"); | ||
| 638 | } | ||
| 639 | |||
| 640 | declarations.AddLine("position = " + std::string(buffer) + '[' + | ||
| 641 | std::to_string(static_cast<u32>(Attribute::Index::Position)) + "];"); | ||
| 642 | |||
| 643 | // If a geometry shader is attached, it will always flip (it's the last stage before | ||
| 644 | // fragment). For more info about flipping, refer to gl_shader_gen.cpp. | ||
| 645 | declarations.AddLine("position.xy *= viewport_flip.xy;"); | ||
| 646 | declarations.AddLine("gl_Position = position;"); | ||
| 647 | declarations.AddLine("position.w = 1.0;"); | ||
| 648 | declarations.AddLine("EmitVertex();"); | ||
| 649 | --declarations.scope; | ||
| 650 | declarations.AddLine('}'); | ||
| 651 | declarations.AddNewLine(); | ||
| 530 | } | 652 | } |
| 531 | 653 | ||
| 532 | private: | ||
| 533 | /// Generates code representing a temporary (GPR) register. | 654 | /// Generates code representing a temporary (GPR) register. |
| 534 | std::string GetRegister(const Register& reg, unsigned elem) { | 655 | std::string GetRegister(const Register& reg, unsigned elem) { |
| 535 | if (reg == Register::ZeroIndex) { | 656 | if (reg == Register::ZeroIndex) { |
| @@ -586,11 +707,19 @@ private: | |||
| 586 | 707 | ||
| 587 | /// Generates code representing an input attribute register. | 708 | /// Generates code representing an input attribute register. |
| 588 | std::string GetInputAttribute(Attribute::Index attribute, | 709 | std::string GetInputAttribute(Attribute::Index attribute, |
| 589 | const Tegra::Shader::IpaMode& input_mode) { | 710 | const Tegra::Shader::IpaMode& input_mode, |
| 711 | boost::optional<Register> vertex = {}) { | ||
| 712 | auto GeometryPass = [&](const std::string& name) { | ||
| 713 | if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) { | ||
| 714 | return "gs_" + name + '[' + GetRegisterAsInteger(vertex.value(), 0, false) + ']'; | ||
| 715 | } | ||
| 716 | return name; | ||
| 717 | }; | ||
| 718 | |||
| 590 | switch (attribute) { | 719 | switch (attribute) { |
| 591 | case Attribute::Index::Position: | 720 | case Attribute::Index::Position: |
| 592 | if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { | 721 | if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { |
| 593 | return "position"; | 722 | return GeometryPass("position"); |
| 594 | } else { | 723 | } else { |
| 595 | return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)"; | 724 | return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)"; |
| 596 | } | 725 | } |
| @@ -619,7 +748,7 @@ private: | |||
| 619 | UNREACHABLE(); | 748 | UNREACHABLE(); |
| 620 | } | 749 | } |
| 621 | } | 750 | } |
| 622 | return "input_attribute_" + std::to_string(index); | 751 | return GeometryPass("input_attribute_" + std::to_string(index)); |
| 623 | } | 752 | } |
| 624 | 753 | ||
| 625 | LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); | 754 | LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); |
| @@ -672,7 +801,7 @@ private: | |||
| 672 | return out; | 801 | return out; |
| 673 | } | 802 | } |
| 674 | 803 | ||
| 675 | /// Generates code representing an output attribute register. | 804 | /// Generates code representing the declaration name of an output attribute register. |
| 676 | std::string GetOutputAttribute(Attribute::Index attribute) { | 805 | std::string GetOutputAttribute(Attribute::Index attribute) { |
| 677 | switch (attribute) { | 806 | switch (attribute) { |
| 678 | case Attribute::Index::Position: | 807 | case Attribute::Index::Position: |
| @@ -708,6 +837,7 @@ private: | |||
| 708 | std::vector<SamplerEntry> used_samplers; | 837 | std::vector<SamplerEntry> used_samplers; |
| 709 | const Maxwell3D::Regs::ShaderStage& stage; | 838 | const Maxwell3D::Regs::ShaderStage& stage; |
| 710 | const std::string& suffix; | 839 | const std::string& suffix; |
| 840 | const Tegra::Shader::Header& header; | ||
| 711 | }; | 841 | }; |
| 712 | 842 | ||
| 713 | class GLSLGenerator { | 843 | class GLSLGenerator { |
| @@ -1103,8 +1233,8 @@ private: | |||
| 1103 | return offset + 1; | 1233 | return offset + 1; |
| 1104 | } | 1234 | } |
| 1105 | 1235 | ||
| 1106 | shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName() + " (" + | 1236 | shader.AddLine( |
| 1107 | std::to_string(instr.value) + ')'); | 1237 | fmt::format("// {}: {} (0x{:016x})", offset, opcode->GetName(), instr.value)); |
| 1108 | 1238 | ||
| 1109 | using Tegra::Shader::Pred; | 1239 | using Tegra::Shader::Pred; |
| 1110 | ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, | 1240 | ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, |
| @@ -1826,7 +1956,7 @@ private: | |||
| 1826 | const auto LoadNextElement = [&](u32 reg_offset) { | 1956 | const auto LoadNextElement = [&](u32 reg_offset) { |
| 1827 | regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, | 1957 | regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, |
| 1828 | static_cast<Attribute::Index>(next_index), | 1958 | static_cast<Attribute::Index>(next_index), |
| 1829 | input_mode); | 1959 | input_mode, instr.gpr39.Value()); |
| 1830 | 1960 | ||
| 1831 | // Load the next attribute element into the following register. If the element | 1961 | // Load the next attribute element into the following register. If the element |
| 1832 | // to load goes beyond the vec4 size, load the first element of the next | 1962 | // to load goes beyond the vec4 size, load the first element of the next |
| @@ -1890,8 +2020,8 @@ private: | |||
| 1890 | 2020 | ||
| 1891 | const auto StoreNextElement = [&](u32 reg_offset) { | 2021 | const auto StoreNextElement = [&](u32 reg_offset) { |
| 1892 | regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), | 2022 | regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), |
| 1893 | next_element, | 2023 | next_element, instr.gpr0.Value() + reg_offset, |
| 1894 | instr.gpr0.Value() + reg_offset); | 2024 | instr.gpr39.Value()); |
| 1895 | 2025 | ||
| 1896 | // Load the next attribute element into the following register. If the element | 2026 | // Load the next attribute element into the following register. If the element |
| 1897 | // to load goes beyond the vec4 size, load the first element of the next | 2027 | // to load goes beyond the vec4 size, load the first element of the next |
| @@ -2299,8 +2429,7 @@ private: | |||
| 2299 | ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), | 2429 | ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), |
| 2300 | "NDV is not implemented"); | 2430 | "NDV is not implemented"); |
| 2301 | 2431 | ||
| 2302 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 2432 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| 2303 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2304 | const bool is_array = instr.tmml.array != 0; | 2433 | const bool is_array = instr.tmml.array != 0; |
| 2305 | auto texture_type = instr.tmml.texture_type.Value(); | 2434 | auto texture_type = instr.tmml.texture_type.Value(); |
| 2306 | const std::string sampler = | 2435 | const std::string sampler = |
| @@ -2311,13 +2440,11 @@ private: | |||
| 2311 | std::string coord; | 2440 | std::string coord; |
| 2312 | switch (texture_type) { | 2441 | switch (texture_type) { |
| 2313 | case Tegra::Shader::TextureType::Texture1D: { | 2442 | case Tegra::Shader::TextureType::Texture1D: { |
| 2314 | std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2315 | coord = "float coords = " + x + ';'; | 2443 | coord = "float coords = " + x + ';'; |
| 2316 | break; | 2444 | break; |
| 2317 | } | 2445 | } |
| 2318 | case Tegra::Shader::TextureType::Texture2D: { | 2446 | case Tegra::Shader::TextureType::Texture2D: { |
| 2319 | std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2447 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2320 | std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2321 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | 2448 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; |
| 2322 | break; | 2449 | break; |
| 2323 | } | 2450 | } |
| @@ -2327,8 +2454,7 @@ private: | |||
| 2327 | UNREACHABLE(); | 2454 | UNREACHABLE(); |
| 2328 | 2455 | ||
| 2329 | // Fallback to interpreting as a 2D texture for now | 2456 | // Fallback to interpreting as a 2D texture for now |
| 2330 | std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2457 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2331 | std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2332 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; | 2458 | coord = "vec2 coords = vec2(" + x + ", " + y + ");"; |
| 2333 | texture_type = Tegra::Shader::TextureType::Texture2D; | 2459 | texture_type = Tegra::Shader::TextureType::Texture2D; |
| 2334 | } | 2460 | } |
| @@ -2738,6 +2864,52 @@ private: | |||
| 2738 | 2864 | ||
| 2739 | break; | 2865 | break; |
| 2740 | } | 2866 | } |
| 2867 | case OpCode::Id::OUT_R: { | ||
| 2868 | ASSERT(instr.gpr20.Value() == Register::ZeroIndex); | ||
| 2869 | ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, | ||
| 2870 | "OUT is expected to be used in a geometry shader."); | ||
| 2871 | |||
| 2872 | if (instr.out.emit) { | ||
| 2873 | // gpr0 is used to store the next address. Hardware returns a pointer but | ||
| 2874 | // we just return the next index with a cyclic cap. | ||
| 2875 | const std::string current{regs.GetRegisterAsInteger(instr.gpr8, 0, false)}; | ||
| 2876 | const std::string next = "((" + current + " + 1" + ") % " + | ||
| 2877 | std::to_string(MAX_GEOMETRY_BUFFERS) + ')'; | ||
| 2878 | shader.AddLine("emit_vertex(" + current + ");"); | ||
| 2879 | regs.SetRegisterToInteger(instr.gpr0, false, 0, next, 1, 1); | ||
| 2880 | } | ||
| 2881 | if (instr.out.cut) { | ||
| 2882 | shader.AddLine("EndPrimitive();"); | ||
| 2883 | } | ||
| 2884 | |||
| 2885 | break; | ||
| 2886 | } | ||
| 2887 | case OpCode::Id::MOV_SYS: { | ||
| 2888 | switch (instr.sys20) { | ||
| 2889 | case Tegra::Shader::SystemVariable::InvocationInfo: { | ||
| 2890 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); | ||
| 2891 | regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1); | ||
| 2892 | break; | ||
| 2893 | } | ||
| 2894 | default: { | ||
| 2895 | LOG_CRITICAL(HW_GPU, "Unhandled system move: {}", | ||
| 2896 | static_cast<u32>(instr.sys20.Value())); | ||
| 2897 | UNREACHABLE(); | ||
| 2898 | } | ||
| 2899 | } | ||
| 2900 | break; | ||
| 2901 | } | ||
| 2902 | case OpCode::Id::ISBERD: { | ||
| 2903 | ASSERT(instr.isberd.o == 0); | ||
| 2904 | ASSERT(instr.isberd.skew == 0); | ||
| 2905 | ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None); | ||
| 2906 | ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None); | ||
| 2907 | ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, | ||
| 2908 | "ISBERD is expected to be used in a geometry shader."); | ||
| 2909 | LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete"); | ||
| 2910 | regs.SetRegisterToFloat(instr.gpr0, 0, regs.GetRegisterAsFloat(instr.gpr8), 1, 1); | ||
| 2911 | break; | ||
| 2912 | } | ||
| 2741 | case OpCode::Id::BRA: { | 2913 | case OpCode::Id::BRA: { |
| 2742 | ASSERT_MSG(instr.bra.constant_buffer == 0, | 2914 | ASSERT_MSG(instr.bra.constant_buffer == 0, |
| 2743 | "BRA with constant buffers are not implemented"); | 2915 | "BRA with constant buffers are not implemented"); |
| @@ -2781,6 +2953,88 @@ private: | |||
| 2781 | LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed"); | 2953 | LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed"); |
| 2782 | break; | 2954 | break; |
| 2783 | } | 2955 | } |
| 2956 | case OpCode::Id::VMAD: { | ||
| 2957 | const bool signed_a = instr.vmad.signed_a == 1; | ||
| 2958 | const bool signed_b = instr.vmad.signed_b == 1; | ||
| 2959 | const bool result_signed = signed_a || signed_b; | ||
| 2960 | boost::optional<std::string> forced_result; | ||
| 2961 | |||
| 2962 | auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed, | ||
| 2963 | Tegra::Shader::VmadType type, u64 byte_height) { | ||
| 2964 | const std::string value = [&]() { | ||
| 2965 | if (!is_chunk) { | ||
| 2966 | const auto offset = static_cast<u32>(byte_height * 8); | ||
| 2967 | return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)"; | ||
| 2968 | } | ||
| 2969 | const std::string zero = "0"; | ||
| 2970 | |||
| 2971 | switch (type) { | ||
| 2972 | case Tegra::Shader::VmadType::Size16_Low: | ||
| 2973 | return '(' + op + " & 0xffff)"; | ||
| 2974 | case Tegra::Shader::VmadType::Size16_High: | ||
| 2975 | return '(' + op + " >> 16)"; | ||
| 2976 | case Tegra::Shader::VmadType::Size32: | ||
| 2977 | // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when | ||
| 2978 | // this type is used (1 * 1 + 0 == 0x5b800000). Until a better | ||
| 2979 | // explanation is found: assert. | ||
| 2980 | UNREACHABLE_MSG("Unimplemented"); | ||
| 2981 | return zero; | ||
| 2982 | case Tegra::Shader::VmadType::Invalid: | ||
| 2983 | // Note(Rodrigo): This flag is invalid according to nvdisasm. From my | ||
| 2984 | // testing (even though it's invalid) this makes the whole instruction | ||
| 2985 | // assign zero to target register. | ||
| 2986 | forced_result = boost::make_optional(zero); | ||
| 2987 | return zero; | ||
| 2988 | default: | ||
| 2989 | UNREACHABLE(); | ||
| 2990 | return zero; | ||
| 2991 | } | ||
| 2992 | }(); | ||
| 2993 | |||
| 2994 | if (is_signed) { | ||
| 2995 | return "int(" + value + ')'; | ||
| 2996 | } | ||
| 2997 | return value; | ||
| 2998 | }; | ||
| 2999 | |||
| 3000 | const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false), | ||
| 3001 | instr.vmad.is_byte_chunk_a != 0, signed_a, | ||
| 3002 | instr.vmad.type_a, instr.vmad.byte_height_a); | ||
| 3003 | |||
| 3004 | std::string op_b; | ||
| 3005 | if (instr.vmad.use_register_b) { | ||
| 3006 | op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false), | ||
| 3007 | instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b, | ||
| 3008 | instr.vmad.byte_height_b); | ||
| 3009 | } else { | ||
| 3010 | op_b = '(' + | ||
| 3011 | std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16()) | ||
| 3012 | : instr.alu.GetImm20_16()) + | ||
| 3013 | ')'; | ||
| 3014 | } | ||
| 3015 | |||
| 3016 | const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed); | ||
| 3017 | |||
| 3018 | std::string result; | ||
| 3019 | if (forced_result) { | ||
| 3020 | result = *forced_result; | ||
| 3021 | } else { | ||
| 3022 | result = '(' + op_a + " * " + op_b + " + " + op_c + ')'; | ||
| 3023 | |||
| 3024 | switch (instr.vmad.shr) { | ||
| 3025 | case Tegra::Shader::VmadShr::Shr7: | ||
| 3026 | result = '(' + result + " >> 7)"; | ||
| 3027 | break; | ||
| 3028 | case Tegra::Shader::VmadShr::Shr15: | ||
| 3029 | result = '(' + result + " >> 15)"; | ||
| 3030 | break; | ||
| 3031 | } | ||
| 3032 | } | ||
| 3033 | regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, | ||
| 3034 | instr.vmad.saturate == 1, 0, Register::Size::Word, | ||
| 3035 | instr.vmad.cc); | ||
| 3036 | break; | ||
| 3037 | } | ||
| 2784 | default: { | 3038 | default: { |
| 2785 | LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); | 3039 | LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); |
| 2786 | UNREACHABLE(); | 3040 | UNREACHABLE(); |
| @@ -2911,7 +3165,7 @@ private: | |||
| 2911 | 3165 | ||
| 2912 | ShaderWriter shader; | 3166 | ShaderWriter shader; |
| 2913 | ShaderWriter declarations; | 3167 | ShaderWriter declarations; |
| 2914 | GLSLRegisterManager regs{shader, declarations, stage, suffix}; | 3168 | GLSLRegisterManager regs{shader, declarations, stage, suffix, header}; |
| 2915 | 3169 | ||
| 2916 | // Declarations | 3170 | // Declarations |
| 2917 | std::set<std::string> declr_predicates; | 3171 | std::set<std::string> declr_predicates; |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index b0466c18f..1e5eb32df 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -17,7 +17,18 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) { | |||
| 17 | std::string out = "#version 430 core\n"; | 17 | std::string out = "#version 430 core\n"; |
| 18 | out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; | 18 | out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; |
| 19 | out += Decompiler::GetCommonDeclarations(); | 19 | out += Decompiler::GetCommonDeclarations(); |
| 20 | out += "bool exec_vertex();\n"; | 20 | |
| 21 | out += R"( | ||
| 22 | out gl_PerVertex { | ||
| 23 | vec4 gl_Position; | ||
| 24 | }; | ||
| 25 | |||
| 26 | layout(std140) uniform vs_config { | ||
| 27 | vec4 viewport_flip; | ||
| 28 | uvec4 instance_id; | ||
| 29 | uvec4 flip_stage; | ||
| 30 | }; | ||
| 31 | )"; | ||
| 21 | 32 | ||
| 22 | if (setup.IsDualProgram()) { | 33 | if (setup.IsDualProgram()) { |
| 23 | out += "bool exec_vertex_b();\n"; | 34 | out += "bool exec_vertex_b();\n"; |
| @@ -28,18 +39,17 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) { | |||
| 28 | Maxwell3D::Regs::ShaderStage::Vertex, "vertex") | 39 | Maxwell3D::Regs::ShaderStage::Vertex, "vertex") |
| 29 | .get_value_or({}); | 40 | .get_value_or({}); |
| 30 | 41 | ||
| 31 | out += R"( | 42 | out += program.first; |
| 32 | |||
| 33 | out gl_PerVertex { | ||
| 34 | vec4 gl_Position; | ||
| 35 | }; | ||
| 36 | 43 | ||
| 37 | out vec4 position; | 44 | if (setup.IsDualProgram()) { |
| 45 | ProgramResult program_b = | ||
| 46 | Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET, | ||
| 47 | Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b") | ||
| 48 | .get_value_or({}); | ||
| 49 | out += program_b.first; | ||
| 50 | } | ||
| 38 | 51 | ||
| 39 | layout (std140) uniform vs_config { | 52 | out += R"( |
| 40 | vec4 viewport_flip; | ||
| 41 | uvec4 instance_id; | ||
| 42 | }; | ||
| 43 | 53 | ||
| 44 | void main() { | 54 | void main() { |
| 45 | position = vec4(0.0, 0.0, 0.0, 0.0); | 55 | position = vec4(0.0, 0.0, 0.0, 0.0); |
| @@ -52,27 +62,52 @@ void main() { | |||
| 52 | 62 | ||
| 53 | out += R"( | 63 | out += R"( |
| 54 | 64 | ||
| 55 | // Viewport can be flipped, which is unsupported by glViewport | 65 | // Check if the flip stage is VertexB |
| 56 | position.xy *= viewport_flip.xy; | 66 | if (flip_stage[0] == 1) { |
| 67 | // Viewport can be flipped, which is unsupported by glViewport | ||
| 68 | position.xy *= viewport_flip.xy; | ||
| 69 | } | ||
| 57 | gl_Position = position; | 70 | gl_Position = position; |
| 58 | 71 | ||
| 59 | // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0 | 72 | // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0 |
| 60 | // For now, this is here to bring order in lieu of proper emulation | 73 | // For now, this is here to bring order in lieu of proper emulation |
| 61 | position.w = 1.0; | 74 | if (flip_stage[0] == 1) { |
| 75 | position.w = 1.0; | ||
| 76 | } | ||
| 62 | } | 77 | } |
| 63 | 78 | ||
| 64 | )"; | 79 | )"; |
| 65 | 80 | ||
| 66 | out += program.first; | 81 | return {out, program.second}; |
| 82 | } | ||
| 67 | 83 | ||
| 68 | if (setup.IsDualProgram()) { | 84 | ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { |
| 69 | ProgramResult program_b = | 85 | std::string out = "#version 430 core\n"; |
| 70 | Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET, | 86 | out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; |
| 71 | Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b") | 87 | out += Decompiler::GetCommonDeclarations(); |
| 72 | .get_value_or({}); | 88 | out += "bool exec_geometry();\n"; |
| 73 | out += program_b.first; | 89 | |
| 74 | } | 90 | ProgramResult program = |
| 91 | Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET, | ||
| 92 | Maxwell3D::Regs::ShaderStage::Geometry, "geometry") | ||
| 93 | .get_value_or({}); | ||
| 94 | out += R"( | ||
| 95 | out gl_PerVertex { | ||
| 96 | vec4 gl_Position; | ||
| 97 | }; | ||
| 75 | 98 | ||
| 99 | layout (std140) uniform gs_config { | ||
| 100 | vec4 viewport_flip; | ||
| 101 | uvec4 instance_id; | ||
| 102 | uvec4 flip_stage; | ||
| 103 | }; | ||
| 104 | |||
| 105 | void main() { | ||
| 106 | exec_geometry(); | ||
| 107 | } | ||
| 108 | |||
| 109 | )"; | ||
| 110 | out += program.first; | ||
| 76 | return {out, program.second}; | 111 | return {out, program.second}; |
| 77 | } | 112 | } |
| 78 | 113 | ||
| @@ -87,7 +122,6 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) { | |||
| 87 | Maxwell3D::Regs::ShaderStage::Fragment, "fragment") | 122 | Maxwell3D::Regs::ShaderStage::Fragment, "fragment") |
| 88 | .get_value_or({}); | 123 | .get_value_or({}); |
| 89 | out += R"( | 124 | out += R"( |
| 90 | in vec4 position; | ||
| 91 | layout(location = 0) out vec4 FragColor0; | 125 | layout(location = 0) out vec4 FragColor0; |
| 92 | layout(location = 1) out vec4 FragColor1; | 126 | layout(location = 1) out vec4 FragColor1; |
| 93 | layout(location = 2) out vec4 FragColor2; | 127 | layout(location = 2) out vec4 FragColor2; |
| @@ -100,6 +134,7 @@ layout(location = 7) out vec4 FragColor7; | |||
| 100 | layout (std140) uniform fs_config { | 134 | layout (std140) uniform fs_config { |
| 101 | vec4 viewport_flip; | 135 | vec4 viewport_flip; |
| 102 | uvec4 instance_id; | 136 | uvec4 instance_id; |
| 137 | uvec4 flip_stage; | ||
| 103 | }; | 138 | }; |
| 104 | 139 | ||
| 105 | void main() { | 140 | void main() { |
| @@ -110,5 +145,4 @@ void main() { | |||
| 110 | out += program.first; | 145 | out += program.first; |
| 111 | return {out, program.second}; | 146 | return {out, program.second}; |
| 112 | } | 147 | } |
| 113 | 148 | } // namespace OpenGL::GLShader \ No newline at end of file | |
| 114 | } // namespace OpenGL::GLShader | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index e56f39e78..79596087a 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -196,6 +196,12 @@ private: | |||
| 196 | ProgramResult GenerateVertexShader(const ShaderSetup& setup); | 196 | ProgramResult GenerateVertexShader(const ShaderSetup& setup); |
| 197 | 197 | ||
| 198 | /** | 198 | /** |
| 199 | * Generates the GLSL geometry shader program source code for the given GS program | ||
| 200 | * @returns String of the shader source code | ||
| 201 | */ | ||
| 202 | ProgramResult GenerateGeometryShader(const ShaderSetup& setup); | ||
| 203 | |||
| 204 | /** | ||
| 199 | * Generates the GLSL fragment shader program source code for the given FS program | 205 | * Generates the GLSL fragment shader program source code for the given FS program |
| 200 | * @returns String of the shader source code | 206 | * @returns String of the shader source code |
| 201 | */ | 207 | */ |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 022d32a86..010857ec6 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp | |||
| @@ -18,6 +18,14 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh | |||
| 18 | 18 | ||
| 19 | // We only assign the instance to the first component of the vector, the rest is just padding. | 19 | // We only assign the instance to the first component of the vector, the rest is just padding. |
| 20 | instance_id[0] = state.current_instance; | 20 | instance_id[0] = state.current_instance; |
| 21 | |||
| 22 | // Assign in which stage the position has to be flipped | ||
| 23 | // (the last stage before the fragment shader). | ||
| 24 | if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) { | ||
| 25 | flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); | ||
| 26 | } else { | ||
| 27 | flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); | ||
| 28 | } | ||
| 21 | } | 29 | } |
| 22 | 30 | ||
| 23 | } // namespace OpenGL::GLShader | 31 | } // namespace OpenGL::GLShader |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 3de15ba9b..b3a191cf2 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h | |||
| @@ -21,8 +21,9 @@ struct MaxwellUniformData { | |||
| 21 | void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); | 21 | void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); |
| 22 | alignas(16) GLvec4 viewport_flip; | 22 | alignas(16) GLvec4 viewport_flip; |
| 23 | alignas(16) GLuvec4 instance_id; | 23 | alignas(16) GLuvec4 instance_id; |
| 24 | alignas(16) GLuvec4 flip_stage; | ||
| 24 | }; | 25 | }; |
| 25 | static_assert(sizeof(MaxwellUniformData) == 32, "MaxwellUniformData structure size is incorrect"); | 26 | static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect"); |
| 26 | static_assert(sizeof(MaxwellUniformData) < 16384, | 27 | static_assert(sizeof(MaxwellUniformData) < 16384, |
| 27 | "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); | 28 | "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); |
| 28 | 29 | ||
| @@ -36,6 +37,10 @@ public: | |||
| 36 | vs = program; | 37 | vs = program; |
| 37 | } | 38 | } |
| 38 | 39 | ||
| 40 | void UseProgrammableGeometryShader(GLuint program) { | ||
| 41 | gs = program; | ||
| 42 | } | ||
| 43 | |||
| 39 | void UseProgrammableFragmentShader(GLuint program) { | 44 | void UseProgrammableFragmentShader(GLuint program) { |
| 40 | fs = program; | 45 | fs = program; |
| 41 | } | 46 | } |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 3d5476e5d..0d2456b56 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <cmath> | 5 | #include <cmath> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include "common/alignment.h" | ||
| 7 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 8 | #include "core/memory.h" | 9 | #include "core/memory.h" |
| 9 | #include "video_core/gpu.h" | 10 | #include "video_core/gpu.h" |
| @@ -199,4 +200,19 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat | |||
| 199 | return rgba_data; | 200 | return rgba_data; |
| 200 | } | 201 | } |
| 201 | 202 | ||
| 203 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | ||
| 204 | u32 block_height, u32 block_depth) { | ||
| 205 | if (tiled) { | ||
| 206 | const u32 gobs_in_x = 64 / bytes_per_pixel; | ||
| 207 | const u32 gobs_in_y = 8; | ||
| 208 | const u32 gobs_in_z = 1; | ||
| 209 | const u32 aligned_width = Common::AlignUp(width, gobs_in_x); | ||
| 210 | const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); | ||
| 211 | const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); | ||
| 212 | return aligned_width * aligned_height * aligned_depth * bytes_per_pixel; | ||
| 213 | } else { | ||
| 214 | return width * height * depth * bytes_per_pixel; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 202 | } // namespace Tegra::Texture | 218 | } // namespace Tegra::Texture |
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index 1f7b731be..234d250af 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h | |||
| @@ -32,4 +32,10 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_ | |||
| 32 | std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, | 32 | std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, |
| 33 | u32 height); | 33 | u32 height); |
| 34 | 34 | ||
| 35 | /** | ||
| 36 | * This function calculates the correct size of a texture depending if it's tiled or not. | ||
| 37 | */ | ||
| 38 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | ||
| 39 | u32 block_height, u32 block_depth); | ||
| 40 | |||
| 35 | } // namespace Tegra::Texture | 41 | } // namespace Tegra::Texture |
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 8f31d825a..58d17abcb 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h | |||
| @@ -161,7 +161,9 @@ struct TICEntry { | |||
| 161 | BitField<21, 3, TICHeaderVersion> header_version; | 161 | BitField<21, 3, TICHeaderVersion> header_version; |
| 162 | }; | 162 | }; |
| 163 | union { | 163 | union { |
| 164 | BitField<0, 3, u32> block_width; | ||
| 164 | BitField<3, 3, u32> block_height; | 165 | BitField<3, 3, u32> block_height; |
| 166 | BitField<6, 3, u32> block_depth; | ||
| 165 | 167 | ||
| 166 | // High 16 bits of the pitch value | 168 | // High 16 bits of the pitch value |
| 167 | BitField<0, 16, u32> pitch_high; | 169 | BitField<0, 16, u32> pitch_high; |
| @@ -202,13 +204,24 @@ struct TICEntry { | |||
| 202 | return depth_minus_1 + 1; | 204 | return depth_minus_1 + 1; |
| 203 | } | 205 | } |
| 204 | 206 | ||
| 207 | u32 BlockWidth() const { | ||
| 208 | ASSERT(IsTiled()); | ||
| 209 | // The block height is stored in log2 format. | ||
| 210 | return 1 << block_width; | ||
| 211 | } | ||
| 212 | |||
| 205 | u32 BlockHeight() const { | 213 | u32 BlockHeight() const { |
| 206 | ASSERT(header_version == TICHeaderVersion::BlockLinear || | 214 | ASSERT(IsTiled()); |
| 207 | header_version == TICHeaderVersion::BlockLinearColorKey); | ||
| 208 | // The block height is stored in log2 format. | 215 | // The block height is stored in log2 format. |
| 209 | return 1 << block_height; | 216 | return 1 << block_height; |
| 210 | } | 217 | } |
| 211 | 218 | ||
| 219 | u32 BlockDepth() const { | ||
| 220 | ASSERT(IsTiled()); | ||
| 221 | // The block height is stored in log2 format. | ||
| 222 | return 1 << block_depth; | ||
| 223 | } | ||
| 224 | |||
| 212 | bool IsTiled() const { | 225 | bool IsTiled() const { |
| 213 | return header_version == TICHeaderVersion::BlockLinear || | 226 | return header_version == TICHeaderVersion::BlockLinear || |
| 214 | header_version == TICHeaderVersion::BlockLinearColorKey; | 227 | header_version == TICHeaderVersion::BlockLinearColorKey; |
diff --git a/src/video_core/utils.h b/src/video_core/utils.h index 681919ae3..237cc1307 100644 --- a/src/video_core/utils.h +++ b/src/video_core/utils.h | |||
| @@ -169,16 +169,20 @@ static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, | |||
| 169 | const std::string nice_addr = fmt::format("0x{:016x}", addr); | 169 | const std::string nice_addr = fmt::format("0x{:016x}", addr); |
| 170 | std::string object_label; | 170 | std::string object_label; |
| 171 | 171 | ||
| 172 | switch (identifier) { | 172 | if (extra_info.empty()) { |
| 173 | case GL_TEXTURE: | 173 | switch (identifier) { |
| 174 | object_label = extra_info + "@" + nice_addr; | 174 | case GL_TEXTURE: |
| 175 | break; | 175 | object_label = "Texture@" + nice_addr; |
| 176 | case GL_PROGRAM: | 176 | break; |
| 177 | object_label = "ShaderProgram@" + nice_addr; | 177 | case GL_PROGRAM: |
| 178 | break; | 178 | object_label = "Shader@" + nice_addr; |
| 179 | default: | 179 | break; |
| 180 | object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr); | 180 | default: |
| 181 | break; | 181 | object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr); |
| 182 | break; | ||
| 183 | } | ||
| 184 | } else { | ||
| 185 | object_label = extra_info + '@' + nice_addr; | ||
| 182 | } | 186 | } |
| 183 | glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); | 187 | glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); |
| 184 | } | 188 | } |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 4e4c108ab..e8ab23326 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -110,6 +110,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | |||
| 110 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, | 110 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, |
| 111 | Common::g_scm_branch, Common::g_scm_desc); | 111 | Common::g_scm_branch, Common::g_scm_desc); |
| 112 | setWindowTitle(QString::fromStdString(window_title)); | 112 | setWindowTitle(QString::fromStdString(window_title)); |
| 113 | setAttribute(Qt::WA_AcceptTouchEvents); | ||
| 113 | 114 | ||
| 114 | InputCommon::Init(); | 115 | InputCommon::Init(); |
| 115 | InputCommon::StartJoystickEventHandler(); | 116 | InputCommon::StartJoystickEventHandler(); |
| @@ -190,11 +191,17 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 190 | return geometry; | 191 | return geometry; |
| 191 | } | 192 | } |
| 192 | 193 | ||
| 193 | qreal GRenderWindow::windowPixelRatio() { | 194 | qreal GRenderWindow::windowPixelRatio() const { |
| 194 | // windowHandle() might not be accessible until the window is displayed to screen. | 195 | // windowHandle() might not be accessible until the window is displayed to screen. |
| 195 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; | 196 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; |
| 196 | } | 197 | } |
| 197 | 198 | ||
| 199 | std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { | ||
| 200 | const qreal pixel_ratio = windowPixelRatio(); | ||
| 201 | return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | ||
| 202 | static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | ||
| 203 | } | ||
| 204 | |||
| 198 | void GRenderWindow::closeEvent(QCloseEvent* event) { | 205 | void GRenderWindow::closeEvent(QCloseEvent* event) { |
| 199 | emit Closed(); | 206 | emit Closed(); |
| 200 | QWidget::closeEvent(event); | 207 | QWidget::closeEvent(event); |
| @@ -209,31 +216,81 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | |||
| 209 | } | 216 | } |
| 210 | 217 | ||
| 211 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | 218 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { |
| 219 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 220 | return; // touch input is handled in TouchBeginEvent | ||
| 221 | |||
| 212 | auto pos = event->pos(); | 222 | auto pos = event->pos(); |
| 213 | if (event->button() == Qt::LeftButton) { | 223 | if (event->button() == Qt::LeftButton) { |
| 214 | qreal pixelRatio = windowPixelRatio(); | 224 | const auto [x, y] = ScaleTouch(pos); |
| 215 | this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), | 225 | this->TouchPressed(x, y); |
| 216 | static_cast<unsigned>(pos.y() * pixelRatio)); | ||
| 217 | } else if (event->button() == Qt::RightButton) { | 226 | } else if (event->button() == Qt::RightButton) { |
| 218 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | 227 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); |
| 219 | } | 228 | } |
| 220 | } | 229 | } |
| 221 | 230 | ||
| 222 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | 231 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { |
| 232 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 233 | return; // touch input is handled in TouchUpdateEvent | ||
| 234 | |||
| 223 | auto pos = event->pos(); | 235 | auto pos = event->pos(); |
| 224 | qreal pixelRatio = windowPixelRatio(); | 236 | const auto [x, y] = ScaleTouch(pos); |
| 225 | this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), | 237 | this->TouchMoved(x, y); |
| 226 | std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); | ||
| 227 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | 238 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); |
| 228 | } | 239 | } |
| 229 | 240 | ||
| 230 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | 241 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { |
| 242 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 243 | return; // touch input is handled in TouchEndEvent | ||
| 244 | |||
| 231 | if (event->button() == Qt::LeftButton) | 245 | if (event->button() == Qt::LeftButton) |
| 232 | this->TouchReleased(); | 246 | this->TouchReleased(); |
| 233 | else if (event->button() == Qt::RightButton) | 247 | else if (event->button() == Qt::RightButton) |
| 234 | InputCommon::GetMotionEmu()->EndTilt(); | 248 | InputCommon::GetMotionEmu()->EndTilt(); |
| 235 | } | 249 | } |
| 236 | 250 | ||
| 251 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | ||
| 252 | // TouchBegin always has exactly one touch point, so take the .first() | ||
| 253 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); | ||
| 254 | this->TouchPressed(x, y); | ||
| 255 | } | ||
| 256 | |||
| 257 | void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) { | ||
| 258 | QPointF pos; | ||
| 259 | int active_points = 0; | ||
| 260 | |||
| 261 | // average all active touch points | ||
| 262 | for (const auto tp : event->touchPoints()) { | ||
| 263 | if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) { | ||
| 264 | active_points++; | ||
| 265 | pos += tp.pos(); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | pos /= active_points; | ||
| 270 | |||
| 271 | const auto [x, y] = ScaleTouch(pos); | ||
| 272 | this->TouchMoved(x, y); | ||
| 273 | } | ||
| 274 | |||
| 275 | void GRenderWindow::TouchEndEvent() { | ||
| 276 | this->TouchReleased(); | ||
| 277 | } | ||
| 278 | |||
| 279 | bool GRenderWindow::event(QEvent* event) { | ||
| 280 | if (event->type() == QEvent::TouchBegin) { | ||
| 281 | TouchBeginEvent(static_cast<QTouchEvent*>(event)); | ||
| 282 | return true; | ||
| 283 | } else if (event->type() == QEvent::TouchUpdate) { | ||
| 284 | TouchUpdateEvent(static_cast<QTouchEvent*>(event)); | ||
| 285 | return true; | ||
| 286 | } else if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) { | ||
| 287 | TouchEndEvent(); | ||
| 288 | return true; | ||
| 289 | } | ||
| 290 | |||
| 291 | return QWidget::event(event); | ||
| 292 | } | ||
| 293 | |||
| 237 | void GRenderWindow::focusOutEvent(QFocusEvent* event) { | 294 | void GRenderWindow::focusOutEvent(QFocusEvent* event) { |
| 238 | QWidget::focusOutEvent(event); | 295 | QWidget::focusOutEvent(event); |
| 239 | InputCommon::GetKeyboard()->ReleaseAllKeys(); | 296 | InputCommon::GetKeyboard()->ReleaseAllKeys(); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index f133bfadf..873985564 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | class QKeyEvent; | 16 | class QKeyEvent; |
| 17 | class QScreen; | 17 | class QScreen; |
| 18 | class QTouchEvent; | ||
| 18 | 19 | ||
| 19 | class GGLWidgetInternal; | 20 | class GGLWidgetInternal; |
| 20 | class GMainWindow; | 21 | class GMainWindow; |
| @@ -119,7 +120,7 @@ public: | |||
| 119 | void restoreGeometry(const QByteArray& geometry); // overridden | 120 | void restoreGeometry(const QByteArray& geometry); // overridden |
| 120 | QByteArray saveGeometry(); // overridden | 121 | QByteArray saveGeometry(); // overridden |
| 121 | 122 | ||
| 122 | qreal windowPixelRatio(); | 123 | qreal windowPixelRatio() const; |
| 123 | 124 | ||
| 124 | void closeEvent(QCloseEvent* event) override; | 125 | void closeEvent(QCloseEvent* event) override; |
| 125 | 126 | ||
| @@ -130,6 +131,8 @@ public: | |||
| 130 | void mouseMoveEvent(QMouseEvent* event) override; | 131 | void mouseMoveEvent(QMouseEvent* event) override; |
| 131 | void mouseReleaseEvent(QMouseEvent* event) override; | 132 | void mouseReleaseEvent(QMouseEvent* event) override; |
| 132 | 133 | ||
| 134 | bool event(QEvent* event) override; | ||
| 135 | |||
| 133 | void focusOutEvent(QFocusEvent* event) override; | 136 | void focusOutEvent(QFocusEvent* event) override; |
| 134 | 137 | ||
| 135 | void OnClientAreaResized(unsigned width, unsigned height); | 138 | void OnClientAreaResized(unsigned width, unsigned height); |
| @@ -148,6 +151,11 @@ signals: | |||
| 148 | void Closed(); | 151 | void Closed(); |
| 149 | 152 | ||
| 150 | private: | 153 | private: |
| 154 | std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||
| 155 | void TouchBeginEvent(const QTouchEvent* event); | ||
| 156 | void TouchUpdateEvent(const QTouchEvent* event); | ||
| 157 | void TouchEndEvent(); | ||
| 158 | |||
| 151 | void OnMinimalClientAreaChangeRequest( | 159 | void OnMinimalClientAreaChangeRequest( |
| 152 | const std::pair<unsigned, unsigned>& minimal_size) override; | 160 | const std::pair<unsigned, unsigned>& minimal_size) override; |
| 153 | 161 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 650dd03c0..7fec15991 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -134,6 +134,7 @@ void Config::ReadValues() { | |||
| 134 | qt_config->beginGroup("Debugging"); | 134 | qt_config->beginGroup("Debugging"); |
| 135 | Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); | 135 | Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); |
| 136 | Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); | 136 | Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); |
| 137 | Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); | ||
| 137 | qt_config->endGroup(); | 138 | qt_config->endGroup(); |
| 138 | 139 | ||
| 139 | qt_config->beginGroup("WebService"); | 140 | qt_config->beginGroup("WebService"); |
| @@ -269,6 +270,7 @@ void Config::SaveValues() { | |||
| 269 | qt_config->beginGroup("Debugging"); | 270 | qt_config->beginGroup("Debugging"); |
| 270 | qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); | 271 | qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); |
| 271 | qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); | 272 | qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); |
| 273 | qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); | ||
| 272 | qt_config->endGroup(); | 274 | qt_config->endGroup(); |
| 273 | 275 | ||
| 274 | qt_config->beginGroup("WebService"); | 276 | qt_config->beginGroup("WebService"); |
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 45d84f19a..9e765fc93 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -33,6 +33,7 @@ void ConfigureDebug::setConfiguration() { | |||
| 33 | ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 33 | ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |
| 34 | ui->toggle_console->setChecked(UISettings::values.show_console); | 34 | ui->toggle_console->setChecked(UISettings::values.show_console); |
| 35 | ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); | 35 | ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); |
| 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); | ||
| 36 | } | 37 | } |
| 37 | 38 | ||
| 38 | void ConfigureDebug::applyConfiguration() { | 39 | void ConfigureDebug::applyConfiguration() { |
| @@ -40,6 +41,7 @@ void ConfigureDebug::applyConfiguration() { | |||
| 40 | Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); | 41 | Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); |
| 41 | UISettings::values.show_console = ui->toggle_console->isChecked(); | 42 | UISettings::values.show_console = ui->toggle_console->isChecked(); |
| 42 | Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); | 43 | Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); |
| 44 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); | ||
| 43 | Debugger::ToggleConsole(); | 45 | Debugger::ToggleConsole(); |
| 44 | Log::Filter filter; | 46 | Log::Filter filter; |
| 45 | filter.ParseFilterString(Settings::values.log_filter); | 47 | filter.ParseFilterString(Settings::values.log_filter); |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 5ae7276bd..ff4987604 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -107,6 +107,29 @@ | |||
| 107 | </widget> | 107 | </widget> |
| 108 | </item> | 108 | </item> |
| 109 | <item> | 109 | <item> |
| 110 | <widget class="QGroupBox" name="groupBox_3"> | ||
| 111 | <property name="title"> | ||
| 112 | <string>Homebrew</string> | ||
| 113 | </property> | ||
| 114 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 115 | <item> | ||
| 116 | <layout class="QHBoxLayout" name="horizontalLayout"> | ||
| 117 | <item> | ||
| 118 | <widget class="QLabel" name="label"> | ||
| 119 | <property name="text"> | ||
| 120 | <string>Arguments String</string> | ||
| 121 | </property> | ||
| 122 | </widget> | ||
| 123 | </item> | ||
| 124 | <item> | ||
| 125 | <widget class="QLineEdit" name="homebrew_args_edit"/> | ||
| 126 | </item> | ||
| 127 | </layout> | ||
| 128 | </item> | ||
| 129 | </layout> | ||
| 130 | </widget> | ||
| 131 | </item> | ||
| 132 | <item> | ||
| 110 | <spacer name="verticalSpacer"> | 133 | <spacer name="verticalSpacer"> |
| 111 | <property name="orientation"> | 134 | <property name="orientation"> |
| 112 | <enum>Qt::Vertical</enum> | 135 | <enum>Qt::Vertical</enum> |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index d2b3de683..8f99a1c78 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -27,9 +27,8 @@ | |||
| 27 | #include "yuzu/ui_settings.h" | 27 | #include "yuzu/ui_settings.h" |
| 28 | 28 | ||
| 29 | namespace { | 29 | namespace { |
| 30 | void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, | 30 | void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca, |
| 31 | const std::shared_ptr<FileSys::NCA>& nca, std::vector<u8>& icon, | 31 | std::vector<u8>& icon, std::string& name) { |
| 32 | std::string& name) { | ||
| 33 | auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); | 32 | auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); |
| 34 | if (icon_file != nullptr) | 33 | if (icon_file != nullptr) |
| 35 | icon = icon_file->ReadAllBytes(); | 34 | icon = icon_file->ReadAllBytes(); |
| @@ -110,7 +109,7 @@ void GameListWorker::AddInstalledTitlesToGameList() { | |||
| 110 | const FileSys::PatchManager patch{program_id}; | 109 | const FileSys::PatchManager patch{program_id}; |
| 111 | const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); | 110 | const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); |
| 112 | if (control != nullptr) | 111 | if (control != nullptr) |
| 113 | GetMetadataFromControlNCA(patch, control, icon, name); | 112 | GetMetadataFromControlNCA(patch, *control, icon, name); |
| 114 | 113 | ||
| 115 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | 114 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |
| 116 | 115 | ||
| @@ -197,8 +196,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 197 | res2 == Loader::ResultStatus::Success) { | 196 | res2 == Loader::ResultStatus::Success) { |
| 198 | // Use from metadata pool. | 197 | // Use from metadata pool. |
| 199 | if (nca_control_map.find(program_id) != nca_control_map.end()) { | 198 | if (nca_control_map.find(program_id) != nca_control_map.end()) { |
| 200 | const auto nca = nca_control_map[program_id]; | 199 | const auto& nca = nca_control_map[program_id]; |
| 201 | GetMetadataFromControlNCA(patch, nca, icon, name); | 200 | GetMetadataFromControlNCA(patch, *nca, icon, name); |
| 202 | } | 201 | } |
| 203 | } | 202 | } |
| 204 | 203 | ||
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 9d934e220..2470f4640 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -138,6 +138,7 @@ void Config::ReadValues() { | |||
| 138 | Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); | 138 | Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); |
| 139 | Settings::values.gdbstub_port = | 139 | Settings::values.gdbstub_port = |
| 140 | static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); | 140 | static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); |
| 141 | Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); | ||
| 141 | 142 | ||
| 142 | // Web Service | 143 | // Web Service |
| 143 | Settings::values.enable_telemetry = | 144 | Settings::values.enable_telemetry = |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 155095095..a9ad92a80 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -40,6 +40,35 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { | |||
| 40 | } | 40 | } |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const { | ||
| 44 | int w, h; | ||
| 45 | SDL_GetWindowSize(render_window, &w, &h); | ||
| 46 | |||
| 47 | touch_x *= w; | ||
| 48 | touch_y *= h; | ||
| 49 | |||
| 50 | return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)), | ||
| 51 | static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))}; | ||
| 52 | } | ||
| 53 | |||
| 54 | void EmuWindow_SDL2::OnFingerDown(float x, float y) { | ||
| 55 | // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind | ||
| 56 | // This isn't critical because the best we can do when we have that is to average them, like the | ||
| 57 | // 3DS does | ||
| 58 | |||
| 59 | const auto [px, py] = TouchToPixelPos(x, y); | ||
| 60 | TouchPressed(px, py); | ||
| 61 | } | ||
| 62 | |||
| 63 | void EmuWindow_SDL2::OnFingerMotion(float x, float y) { | ||
| 64 | const auto [px, py] = TouchToPixelPos(x, y); | ||
| 65 | TouchMoved(px, py); | ||
| 66 | } | ||
| 67 | |||
| 68 | void EmuWindow_SDL2::OnFingerUp() { | ||
| 69 | TouchReleased(); | ||
| 70 | } | ||
| 71 | |||
| 43 | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { | 72 | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { |
| 44 | if (state == SDL_PRESSED) { | 73 | if (state == SDL_PRESSED) { |
| 45 | InputCommon::GetKeyboard()->PressKey(key); | 74 | InputCommon::GetKeyboard()->PressKey(key); |
| @@ -219,11 +248,26 @@ void EmuWindow_SDL2::PollEvents() { | |||
| 219 | OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); | 248 | OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); |
| 220 | break; | 249 | break; |
| 221 | case SDL_MOUSEMOTION: | 250 | case SDL_MOUSEMOTION: |
| 222 | OnMouseMotion(event.motion.x, event.motion.y); | 251 | // ignore if it came from touch |
| 252 | if (event.button.which != SDL_TOUCH_MOUSEID) | ||
| 253 | OnMouseMotion(event.motion.x, event.motion.y); | ||
| 223 | break; | 254 | break; |
| 224 | case SDL_MOUSEBUTTONDOWN: | 255 | case SDL_MOUSEBUTTONDOWN: |
| 225 | case SDL_MOUSEBUTTONUP: | 256 | case SDL_MOUSEBUTTONUP: |
| 226 | OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y); | 257 | // ignore if it came from touch |
| 258 | if (event.button.which != SDL_TOUCH_MOUSEID) { | ||
| 259 | OnMouseButton(event.button.button, event.button.state, event.button.x, | ||
| 260 | event.button.y); | ||
| 261 | } | ||
| 262 | break; | ||
| 263 | case SDL_FINGERDOWN: | ||
| 264 | OnFingerDown(event.tfinger.x, event.tfinger.y); | ||
| 265 | break; | ||
| 266 | case SDL_FINGERMOTION: | ||
| 267 | OnFingerMotion(event.tfinger.x, event.tfinger.y); | ||
| 268 | break; | ||
| 269 | case SDL_FINGERUP: | ||
| 270 | OnFingerUp(); | ||
| 227 | break; | 271 | break; |
| 228 | case SDL_QUIT: | 272 | case SDL_QUIT: |
| 229 | is_open = false; | 273 | is_open = false; |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index d34902109..b0d4116cc 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -40,6 +40,18 @@ private: | |||
| 40 | /// Called by PollEvents when a mouse button is pressed or released | 40 | /// Called by PollEvents when a mouse button is pressed or released |
| 41 | void OnMouseButton(u32 button, u8 state, s32 x, s32 y); | 41 | void OnMouseButton(u32 button, u8 state, s32 x, s32 y); |
| 42 | 42 | ||
| 43 | /// Translates pixel position (0..1) to pixel positions | ||
| 44 | std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; | ||
| 45 | |||
| 46 | /// Called by PollEvents when a finger starts touching the touchscreen | ||
| 47 | void OnFingerDown(float x, float y); | ||
| 48 | |||
| 49 | /// Called by PollEvents when a finger moves while touching the touchscreen | ||
| 50 | void OnFingerMotion(float x, float y); | ||
| 51 | |||
| 52 | /// Called by PollEvents when a finger stops touching the touchscreen | ||
| 53 | void OnFingerUp(); | ||
| 54 | |||
| 43 | /// Called by PollEvents when any event that may cause the window to be resized occurs | 55 | /// Called by PollEvents when any event that may cause the window to be resized occurs |
| 44 | void OnResize(); | 56 | void OnResize(); |
| 45 | 57 | ||
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 1d951ca3f..27aba95f6 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -56,9 +56,10 @@ static void PrintHelp(const char* argv0) { | |||
| 56 | std::cout << "Usage: " << argv0 | 56 | std::cout << "Usage: " << argv0 |
| 57 | << " [options] <filename>\n" | 57 | << " [options] <filename>\n" |
| 58 | "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" | 58 | "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" |
| 59 | "-f, --fullscreen Start in fullscreen mode\n" | 59 | "-f, --fullscreen Start in fullscreen mode\n" |
| 60 | "-h, --help Display this help and exit\n" | 60 | "-h, --help Display this help and exit\n" |
| 61 | "-v, --version Output version information and exit\n"; | 61 | "-v, --version Output version information and exit\n" |
| 62 | "-p, --program Pass following string as arguments to executable\n"; | ||
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | static void PrintVersion() { | 65 | static void PrintVersion() { |
| @@ -103,15 +104,13 @@ int main(int argc, char** argv) { | |||
| 103 | bool fullscreen = false; | 104 | bool fullscreen = false; |
| 104 | 105 | ||
| 105 | static struct option long_options[] = { | 106 | static struct option long_options[] = { |
| 106 | {"gdbport", required_argument, 0, 'g'}, | 107 | {"gdbport", required_argument, 0, 'g'}, {"fullscreen", no_argument, 0, 'f'}, |
| 107 | {"fullscreen", no_argument, 0, 'f'}, | 108 | {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'}, |
| 108 | {"help", no_argument, 0, 'h'}, | 109 | {"program", optional_argument, 0, 'p'}, {0, 0, 0, 0}, |
| 109 | {"version", no_argument, 0, 'v'}, | ||
| 110 | {0, 0, 0, 0}, | ||
| 111 | }; | 110 | }; |
| 112 | 111 | ||
| 113 | while (optind < argc) { | 112 | while (optind < argc) { |
| 114 | char arg = getopt_long(argc, argv, "g:fhv", long_options, &option_index); | 113 | char arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index); |
| 115 | if (arg != -1) { | 114 | if (arg != -1) { |
| 116 | switch (arg) { | 115 | switch (arg) { |
| 117 | case 'g': | 116 | case 'g': |
| @@ -135,6 +134,10 @@ int main(int argc, char** argv) { | |||
| 135 | case 'v': | 134 | case 'v': |
| 136 | PrintVersion(); | 135 | PrintVersion(); |
| 137 | return 0; | 136 | return 0; |
| 137 | case 'p': | ||
| 138 | Settings::values.program_args = argv[optind]; | ||
| 139 | ++optind; | ||
| 140 | break; | ||
| 138 | } | 141 | } |
| 139 | } else { | 142 | } else { |
| 140 | #ifdef _WIN32 | 143 | #ifdef _WIN32 |