summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/kernel/errors.h74
-rw-r--r--src/core/hle/kernel/process.cpp4
-rw-r--r--src/core/hle/kernel/process.h3
-rw-r--r--src/core/hle/kernel/svc.cpp22
-rw-r--r--src/core/hle/kernel/vm_manager.cpp6
-rw-r--r--src/core/hle/kernel/vm_manager.h3
-rw-r--r--src/core/hle/service/am/am.cpp27
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp38
-rw-r--r--src/core/hle/service/btm/btm.cpp108
-rw-r--r--src/core/hle/service/ldr/ldr.cpp397
-rw-r--r--src/core/hle/service/ns/pl_u.cpp8
-rw-r--r--src/core/hle/service/time/interface.cpp3
-rw-r--r--src/core/hle/service/time/time.cpp15
-rw-r--r--src/core/hle/service/time/time.h1
-rw-r--r--src/core/hle/service/vi/vi.cpp22
-rw-r--r--src/core/loader/nro.cpp7
-rw-r--r--src/video_core/engines/maxwell_3d.cpp7
-rw-r--r--src/video_core/engines/maxwell_3d.h142
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp161
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h12
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h1
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp152
-rw-r--r--src/video_core/renderer_opengl/gl_state.h32
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h6
-rw-r--r--src/video_core/textures/decoders.cpp15
-rw-r--r--src/video_core/textures/decoders.h7
-rw-r--r--src/video_core/textures/texture.h15
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp14
-rw-r--r--src/yuzu/configuration/configure_gamelist.h2
-rw-r--r--src/yuzu/configuration/configure_general.cpp3
-rw-r--r--src/yuzu/main.cpp18
-rw-r--r--src/yuzu/ui_settings.h2
-rw-r--r--src/yuzu_cmd/config.cpp2
36 files changed, 1016 insertions, 323 deletions
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index ee698c8a7..8b58d701d 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -8,58 +8,28 @@
8 8
9namespace Kernel { 9namespace Kernel {
10 10
11namespace ErrCodes { 11// Confirmed Switch kernel error codes
12enum {
13 // Confirmed Switch OS error codes
14 MaxConnectionsReached = 7,
15 InvalidSize = 101,
16 InvalidAddress = 102,
17 HandleTableFull = 105,
18 InvalidMemoryState = 106,
19 InvalidMemoryPermissions = 108,
20 InvalidMemoryRange = 110,
21 InvalidThreadPriority = 112,
22 InvalidProcessorId = 113,
23 InvalidHandle = 114,
24 InvalidPointer = 115,
25 InvalidCombination = 116,
26 Timeout = 117,
27 SynchronizationCanceled = 118,
28 TooLarge = 119,
29 InvalidEnumValue = 120,
30 NoSuchEntry = 121,
31 AlreadyRegistered = 122,
32 SessionClosed = 123,
33 InvalidState = 125,
34 ResourceLimitExceeded = 132,
35};
36}
37 12
38// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always 13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
39// double check that the code matches before re-using the constant. 14constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
40 15constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
41constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); 16constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
42constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed); 17constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
43constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); 18constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
44constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, 19constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
45 ErrCodes::MaxConnectionsReached); 20constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
46constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); 21constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
47constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, 22constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
48 ErrCodes::InvalidCombination); 23constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
49constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); 24constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
50constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); 25constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
51constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, 26constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
52 ErrCodes::InvalidMemoryPermissions); 27constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
53constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange); 28constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
54constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 29constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
55constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 30constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
56constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); 31constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
57constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered); 32constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
58constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); 33constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
59constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
60 ErrCodes::InvalidThreadPriority);
61constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
62constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
63constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
64 34
65} // namespace Kernel 35} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index f06b6bb55..a257c3726 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -252,8 +252,8 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
252 return vm_manager.HeapFree(target, size); 252 return vm_manager.HeapFree(target, size);
253} 253}
254 254
255ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 255ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
256 return vm_manager.MirrorMemory(dst_addr, src_addr, size); 256 return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
257} 257}
258 258
259ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { 259ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index cf48787ce..230e395ff 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -259,7 +259,8 @@ public:
259 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); 259 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
260 ResultCode HeapFree(VAddr target, u32 size); 260 ResultCode HeapFree(VAddr target, u32 size);
261 261
262 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); 262 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
263 MemoryState state = MemoryState::Mapped);
263 264
264 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); 265 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
265 266
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 5f4521122..75dbfc31d 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -214,7 +214,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
214 // Read 1 char beyond the max allowed port name to detect names that are too long. 214 // Read 1 char beyond the max allowed port name to detect names that are too long.
215 std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); 215 std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
216 if (port_name.size() > PortNameMaxLength) { 216 if (port_name.size() > PortNameMaxLength) {
217 return ERR_PORT_NAME_TOO_LONG; 217 return ERR_OUT_OF_RANGE;
218 } 218 }
219 219
220 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); 220 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
@@ -310,8 +310,9 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
310 310
311 static constexpr u64 MaxHandles = 0x40; 311 static constexpr u64 MaxHandles = 0x40;
312 312
313 if (handle_count > MaxHandles) 313 if (handle_count > MaxHandles) {
314 return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge); 314 return ERR_OUT_OF_RANGE;
315 }
315 316
316 auto* const thread = GetCurrentThread(); 317 auto* const thread = GetCurrentThread();
317 318
@@ -376,8 +377,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
376 } 377 }
377 378
378 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); 379 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
379 thread->SetWaitSynchronizationResult( 380 thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
380 ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
381 thread->ResumeFromWait(); 381 thread->ResumeFromWait();
382 return RESULT_SUCCESS; 382 return RESULT_SUCCESS;
383} 383}
@@ -606,7 +606,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
606 } 606 }
607 607
608 if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) { 608 if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
609 return ERR_INVALID_COMBINATION_KERNEL; 609 return ERR_INVALID_COMBINATION;
610 } 610 }
611 611
612 *result = current_process->GetRandomEntropy(info_sub_id); 612 *result = current_process->GetRandomEntropy(info_sub_id);
@@ -643,7 +643,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
643 case GetInfoType::ThreadTickCount: { 643 case GetInfoType::ThreadTickCount: {
644 constexpr u64 num_cpus = 4; 644 constexpr u64 num_cpus = 4;
645 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { 645 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
646 return ERR_INVALID_COMBINATION_KERNEL; 646 return ERR_INVALID_COMBINATION;
647 } 647 }
648 648
649 const auto thread = 649 const auto thread =
@@ -1181,7 +1181,7 @@ static ResultCode CloseHandle(Handle handle) {
1181 1181
1182/// Reset an event 1182/// Reset an event
1183static ResultCode ResetSignal(Handle handle) { 1183static ResultCode ResetSignal(Handle handle) {
1184 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 1184 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
1185 1185
1186 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1186 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1187 auto event = handle_table.Get<Event>(handle); 1187 auto event = handle_table.Get<Event>(handle);
@@ -1236,7 +1236,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1236 } 1236 }
1237 1237
1238 if (mask == 0) { 1238 if (mask == 0) {
1239 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 1239 return ERR_INVALID_COMBINATION;
1240 } 1240 }
1241 1241
1242 /// This value is used to only change the affinity mask without changing the current ideal core. 1242 /// This value is used to only change the affinity mask without changing the current ideal core.
@@ -1245,12 +1245,12 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1245 if (core == OnlyChangeMask) { 1245 if (core == OnlyChangeMask) {
1246 core = thread->GetIdealCore(); 1246 core = thread->GetIdealCore();
1247 } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) { 1247 } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
1248 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 1248 return ERR_INVALID_PROCESSOR_ID;
1249 } 1249 }
1250 1250
1251 // Error out if the input core isn't enabled in the input mask. 1251 // Error out if the input core isn't enabled in the input mask.
1252 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) { 1252 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
1253 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 1253 return ERR_INVALID_COMBINATION;
1254 } 1254 }
1255 1255
1256 thread->ChangeCore(core, mask); 1256 thread->ChangeCore(core, mask);
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index ec7fd6150..100f8f6bf 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -298,7 +298,7 @@ ResultCode VMManager::HeapFree(VAddr target, u64 size) {
298 return RESULT_SUCCESS; 298 return RESULT_SUCCESS;
299} 299}
300 300
301ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 301ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
302 const auto vma = FindVMA(src_addr); 302 const auto vma = FindVMA(src_addr);
303 303
304 ASSERT_MSG(vma != vma_map.end(), "Invalid memory address"); 304 ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
@@ -312,8 +312,8 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
312 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; 312 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
313 const std::size_t backing_block_offset = vma->second.offset + vma_offset; 313 const std::size_t backing_block_offset = vma->second.offset + vma_offset;
314 314
315 CASCADE_RESULT(auto new_vma, MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, 315 CASCADE_RESULT(auto new_vma,
316 MemoryState::Mapped)); 316 MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state));
317 // Protect mirror with permissions from old region 317 // Protect mirror with permissions from old region
318 Reprotect(new_vma, vma->second.permissions); 318 Reprotect(new_vma, vma->second.permissions);
319 // Remove permissions from old region 319 // Remove permissions from old region
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 248cc46dc..d522404fe 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -189,7 +189,8 @@ public:
189 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); 189 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
190 ResultCode HeapFree(VAddr target, u64 size); 190 ResultCode HeapFree(VAddr target, u64 size);
191 191
192 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); 192 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
193 MemoryState state = MemoryState::Mapped);
193 194
194 /** 195 /**
195 * Scans all VMAs and updates the page table range of any that use the given vector as backing 196 * Scans all VMAs and updates the page table range of any that use the given vector as backing
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 0477ce66e..3758ecae1 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -203,8 +203,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
203ISelfController::~ISelfController() = default; 203ISelfController::~ISelfController() = default;
204 204
205void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { 205void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
206 // Takes 3 input u8s with each field located immediately after the previous u8, these are 206 // Takes 3 input u8s with each field located immediately after the previous
207 // bool flags. No output. 207 // u8, these are bool flags. No output.
208 208
209 IPC::RequestParser rp{ctx}; 209 IPC::RequestParser rp{ctx};
210 210
@@ -258,8 +258,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
258} 258}
259 259
260void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { 260void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
261 // Takes 3 input u8s with each field located immediately after the previous u8, these are 261 // Takes 3 input u8s with each field located immediately after the previous
262 // bool flags. No output. 262 // u8, these are bool flags. No output.
263 IPC::RequestParser rp{ctx}; 263 IPC::RequestParser rp{ctx};
264 264
265 bool enabled = rp.Pop<bool>(); 265 bool enabled = rp.Pop<bool>();
@@ -302,8 +302,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
302} 302}
303 303
304void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { 304void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
305 // TODO(Subv): Find out how AM determines the display to use, for now just create the layer 305 // TODO(Subv): Find out how AM determines the display to use, for now just
306 // in the Default display. 306 // create the layer in the Default display.
307 u64 display_id = nvflinger->OpenDisplay("Default"); 307 u64 display_id = nvflinger->OpenDisplay("Default");
308 u64 layer_id = nvflinger->CreateLayer(display_id); 308 u64 layer_id = nvflinger->CreateLayer(display_id);
309 309
@@ -733,7 +733,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
733 {70, nullptr, "RequestToShutdown"}, 733 {70, nullptr, "RequestToShutdown"},
734 {71, nullptr, "RequestToReboot"}, 734 {71, nullptr, "RequestToReboot"},
735 {80, nullptr, "ExitAndRequestToShowThanksMessage"}, 735 {80, nullptr, "ExitAndRequestToShowThanksMessage"},
736 {90, nullptr, "EnableApplicationCrashReport"}, 736 {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
737 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"}, 737 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
738 {101, nullptr, "SetApplicationCopyrightImage"}, 738 {101, nullptr, "SetApplicationCopyrightImage"},
739 {102, nullptr, "SetApplicationCopyrightVisibility"}, 739 {102, nullptr, "SetApplicationCopyrightVisibility"},
@@ -752,6 +752,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
752 752
753IApplicationFunctions::~IApplicationFunctions() = default; 753IApplicationFunctions::~IApplicationFunctions() = default;
754 754
755void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
756 IPC::ResponseBuilder rb{ctx, 2};
757 rb.Push(RESULT_SUCCESS);
758 LOG_WARNING(Service_AM, "(STUBBED) called");
759}
760
755void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed( 761void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
756 Kernel::HLERequestContext& ctx) { 762 Kernel::HLERequestContext& ctx) {
757 IPC::ResponseBuilder rb{ctx, 2}; 763 IPC::ResponseBuilder rb{ctx, 2};
@@ -821,7 +827,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
821 827
822void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { 828void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
823 // Takes an input u32 Result, no output. 829 // Takes an input u32 Result, no output.
824 // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. 830 // For example, in some cases official apps use this with error 0x2A2 then
831 // uses svcBreak.
825 832
826 IPC::RequestParser rp{ctx}; 833 IPC::RequestParser rp{ctx};
827 u32 result = rp.Pop<u32>(); 834 u32 result = rp.Pop<u32>();
@@ -884,8 +891,8 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
884void InstallInterfaces(SM::ServiceManager& service_manager, 891void InstallInterfaces(SM::ServiceManager& service_manager,
885 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { 892 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
886 auto message_queue = std::make_shared<AppletMessageQueue>(); 893 auto message_queue = std::make_shared<AppletMessageQueue>();
887 message_queue->PushMessage( 894 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
888 AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on game boot 895 // game boot
889 896
890 std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager); 897 std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
891 std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager); 898 std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 2f1c20bce..5a3fcba8f 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -185,6 +185,7 @@ private:
185 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); 185 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
186 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); 186 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
187 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); 187 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
188 void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
188}; 189};
189 190
190class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { 191class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 783c39503..763e619a4 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -77,8 +77,8 @@ private:
77 IPC::ResponseBuilder rb{ctx, 6}; 77 IPC::ResponseBuilder rb{ctx, 6};
78 rb.Push(RESULT_SUCCESS); 78 rb.Push(RESULT_SUCCESS);
79 rb.Push<u32>(consumed); 79 rb.Push<u32>(consumed);
80 rb.Push<u64>(performance);
81 rb.Push<u32>(sample_count); 80 rb.Push<u32>(sample_count);
81 rb.Push<u64>(performance);
82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
83 } 83 }
84 84
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index d0a15cc4c..f3bde6d0d 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -2,12 +2,49 @@
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/logging/log.h"
6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/event.h"
8#include "core/hle/kernel/hle_ipc.h"
5#include "core/hle/service/btdrv/btdrv.h" 9#include "core/hle/service/btdrv/btdrv.h"
6#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
7#include "core/hle/service/sm/sm.h" 11#include "core/hle/service/sm/sm.h"
8 12
9namespace Service::BtDrv { 13namespace Service::BtDrv {
10 14
15class Bt final : public ServiceFramework<Bt> {
16public:
17 explicit Bt() : ServiceFramework{"bt"} {
18 // clang-format off
19 static const FunctionInfo functions[] = {
20 {0, nullptr, "Unknown0"},
21 {1, nullptr, "Unknown1"},
22 {2, nullptr, "Unknown2"},
23 {3, nullptr, "Unknown3"},
24 {4, nullptr, "Unknown4"},
25 {5, nullptr, "Unknown5"},
26 {6, nullptr, "Unknown6"},
27 {7, nullptr, "Unknown7"},
28 {8, nullptr, "Unknown8"},
29 {9, &Bt::RegisterEvent, "RegisterEvent"},
30 };
31 // clang-format on
32 RegisterHandlers(functions);
33 }
34
35private:
36 void RegisterEvent(Kernel::HLERequestContext& ctx) {
37 auto& kernel = Core::System::GetInstance().Kernel();
38 register_event =
39 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "BT:RegisterEvent");
40 IPC::ResponseBuilder rb{ctx, 2, 1};
41 rb.Push(RESULT_SUCCESS);
42 rb.PushCopyObjects(register_event);
43 LOG_WARNING(Service_BTM, "(STUBBED) called");
44 }
45 Kernel::SharedPtr<Kernel::Event> register_event;
46};
47
11class BtDrv final : public ServiceFramework<BtDrv> { 48class BtDrv final : public ServiceFramework<BtDrv> {
12public: 49public:
13 explicit BtDrv() : ServiceFramework{"btdrv"} { 50 explicit BtDrv() : ServiceFramework{"btdrv"} {
@@ -67,6 +104,7 @@ public:
67 104
68void InstallInterfaces(SM::ServiceManager& sm) { 105void InstallInterfaces(SM::ServiceManager& sm) {
69 std::make_shared<BtDrv>()->InstallAsService(sm); 106 std::make_shared<BtDrv>()->InstallAsService(sm);
107 std::make_shared<Bt>()->InstallAsService(sm);
70} 108}
71 109
72} // namespace Service::BtDrv 110} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index b949bfabd..a02f6b53a 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -6,13 +6,118 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/event.h"
9#include "core/hle/kernel/hle_ipc.h" 10#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/service/btm/btm.h" 11#include "core/hle/service/btm/btm.h"
11#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13 13
14namespace Service::BTM { 14namespace Service::BTM {
15 15
16class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
17public:
18 explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
22 {1, nullptr, "Unknown1"},
23 {2, nullptr, "Unknown2"},
24 {3, nullptr, "Unknown3"},
25 {4, nullptr, "Unknown4"},
26 {5, nullptr, "Unknown5"},
27 {6, nullptr, "Unknown6"},
28 {7, nullptr, "Unknown7"},
29 {8, nullptr, "Unknown8"},
30 {9, nullptr, "Unknown9"},
31 {10, nullptr, "Unknown10"},
32 {17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
33 {18, nullptr, "Unknown18"},
34 {19, nullptr, "Unknown19"},
35 {20, nullptr, "Unknown20"},
36 {21, nullptr, "Unknown21"},
37 {22, nullptr, "Unknown22"},
38 {23, nullptr, "Unknown23"},
39 {24, nullptr, "Unknown24"},
40 {25, nullptr, "Unknown25"},
41 {26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
42 {27, nullptr, "Unknown27"},
43 {28, nullptr, "Unknown28"},
44 {29, nullptr, "Unknown29"},
45 {30, nullptr, "Unknown30"},
46 {31, nullptr, "Unknown31"},
47 {32, nullptr, "Unknown32"},
48 {33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
49 {34, nullptr, "Unknown34"},
50 {35, nullptr, "Unknown35"},
51 {36, nullptr, "Unknown36"},
52 {37, nullptr, "Unknown37"},
53 };
54 // clang-format on
55 RegisterHandlers(functions);
56 }
57
58private:
59 void GetScanEvent(Kernel::HLERequestContext& ctx) {
60 auto& kernel = Core::System::GetInstance().Kernel();
61 scan_event =
62 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ScanEvent");
63 IPC::ResponseBuilder rb{ctx, 2, 1};
64 rb.Push(RESULT_SUCCESS);
65 rb.PushCopyObjects(scan_event);
66 LOG_WARNING(Service_BTM, "(STUBBED) called");
67 }
68 void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
69 auto& kernel = Core::System::GetInstance().Kernel();
70 connection_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
71 "IBtmUserCore:ConnectionEvent");
72 IPC::ResponseBuilder rb{ctx, 2, 1};
73 rb.Push(RESULT_SUCCESS);
74 rb.PushCopyObjects(connection_event);
75 LOG_WARNING(Service_BTM, "(STUBBED) called");
76 }
77 void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
78 auto& kernel = Core::System::GetInstance().Kernel();
79 service_discovery =
80 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
81 IPC::ResponseBuilder rb{ctx, 2, 1};
82 rb.Push(RESULT_SUCCESS);
83 rb.PushCopyObjects(service_discovery);
84 LOG_WARNING(Service_BTM, "(STUBBED) called");
85 }
86 void GetConfigEvent(Kernel::HLERequestContext& ctx) {
87 auto& kernel = Core::System::GetInstance().Kernel();
88 config_event =
89 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConfigEvent");
90 IPC::ResponseBuilder rb{ctx, 2, 1};
91 rb.Push(RESULT_SUCCESS);
92 rb.PushCopyObjects(config_event);
93 LOG_WARNING(Service_BTM, "(STUBBED) called");
94 }
95 Kernel::SharedPtr<Kernel::Event> scan_event;
96 Kernel::SharedPtr<Kernel::Event> connection_event;
97 Kernel::SharedPtr<Kernel::Event> service_discovery;
98 Kernel::SharedPtr<Kernel::Event> config_event;
99};
100
101class BTM_USR final : public ServiceFramework<BTM_USR> {
102public:
103 explicit BTM_USR() : ServiceFramework{"btm:u"} {
104 // clang-format off
105 static const FunctionInfo functions[] = {
106 {0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
107 };
108 // clang-format on
109 RegisterHandlers(functions);
110 }
111
112private:
113 void GetCoreImpl(Kernel::HLERequestContext& ctx) {
114 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
115 rb.Push(RESULT_SUCCESS);
116 rb.PushIpcInterface<IBtmUserCore>();
117 LOG_DEBUG(Service_BTM, "called");
118 }
119};
120
16class BTM final : public ServiceFramework<BTM> { 121class BTM final : public ServiceFramework<BTM> {
17public: 122public:
18 explicit BTM() : ServiceFramework{"btm"} { 123 explicit BTM() : ServiceFramework{"btm"} {
@@ -116,6 +221,7 @@ void InstallInterfaces(SM::ServiceManager& sm) {
116 std::make_shared<BTM>()->InstallAsService(sm); 221 std::make_shared<BTM>()->InstallAsService(sm);
117 std::make_shared<BTM_DBG>()->InstallAsService(sm); 222 std::make_shared<BTM_DBG>()->InstallAsService(sm);
118 std::make_shared<BTM_SYS>()->InstallAsService(sm); 223 std::make_shared<BTM_SYS>()->InstallAsService(sm);
224 std::make_shared<BTM_USR>()->InstallAsService(sm);
119} 225}
120 226
121} // namespace Service::BTM 227} // namespace Service::BTM
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index d607d985e..b43f1f054 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -4,7 +4,10 @@
4 4
5#include <memory> 5#include <memory>
6#include <fmt/format.h> 6#include <fmt/format.h>
7#include <mbedtls/sha256.h>
7 8
9#include "common/alignment.h"
10#include "common/hex_util.h"
8#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
10#include "core/hle/service/ldr/ldr.h" 13#include "core/hle/service/ldr/ldr.h"
@@ -13,6 +16,38 @@
13 16
14namespace Service::LDR { 17namespace Service::LDR {
15 18
19namespace ErrCodes {
20enum {
21 InvalidMemoryState = 51,
22 InvalidNRO = 52,
23 InvalidNRR = 53,
24 MissingNRRHash = 54,
25 MaximumNRO = 55,
26 MaximumNRR = 56,
27 AlreadyLoaded = 57,
28 InvalidAlignment = 81,
29 InvalidSize = 82,
30 InvalidNROAddress = 84,
31 InvalidNRRAddress = 85,
32 NotInitialized = 87,
33};
34}
35
36constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState);
37constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO);
38constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR);
39constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash);
40constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO);
41constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR);
42constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded);
43constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment);
44constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize);
45constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress);
46constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress);
47constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized);
48
49constexpr u64 MAXIMUM_LOADED_RO = 0x40;
50
16class DebugMonitor final : public ServiceFramework<DebugMonitor> { 51class DebugMonitor final : public ServiceFramework<DebugMonitor> {
17public: 52public:
18 explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} { 53 explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} {
@@ -64,9 +99,9 @@ public:
64 // clang-format off 99 // clang-format off
65 static const FunctionInfo functions[] = { 100 static const FunctionInfo functions[] = {
66 {0, &RelocatableObject::LoadNro, "LoadNro"}, 101 {0, &RelocatableObject::LoadNro, "LoadNro"},
67 {1, nullptr, "UnloadNro"}, 102 {1, &RelocatableObject::UnloadNro, "UnloadNro"},
68 {2, &RelocatableObject::LoadNrr, "LoadNrr"}, 103 {2, &RelocatableObject::LoadNrr, "LoadNrr"},
69 {3, nullptr, "UnloadNrr"}, 104 {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
70 {4, &RelocatableObject::Initialize, "Initialize"}, 105 {4, &RelocatableObject::Initialize, "Initialize"},
71 }; 106 };
72 // clang-format on 107 // clang-format on
@@ -75,9 +110,123 @@ public:
75 } 110 }
76 111
77 void LoadNrr(Kernel::HLERequestContext& ctx) { 112 void LoadNrr(Kernel::HLERequestContext& ctx) {
113 IPC::RequestParser rp{ctx};
114 rp.Skip(2, false);
115 const VAddr nrr_addr{rp.Pop<VAddr>()};
116 const u64 nrr_size{rp.Pop<u64>()};
117
118 if (!initialized) {
119 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
120 IPC::ResponseBuilder rb{ctx, 2};
121 rb.Push(ERROR_NOT_INITIALIZED);
122 return;
123 }
124
125 if (nrr.size() >= MAXIMUM_LOADED_RO) {
126 LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs "
127 "(0x40)! Failing...");
128 IPC::ResponseBuilder rb{ctx, 2};
129 rb.Push(ERROR_MAXIMUM_NRR);
130 return;
131 }
132
133 // NRR Address does not fall on 0x1000 byte boundary
134 if (!Common::Is4KBAligned(nrr_addr)) {
135 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
136 IPC::ResponseBuilder rb{ctx, 2};
137 rb.Push(ERROR_INVALID_ALIGNMENT);
138 return;
139 }
140
141 // NRR Size is zero or causes overflow
142 if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) {
143 LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
144 nrr_addr, nrr_size);
145 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(ERROR_INVALID_SIZE);
147 return;
148 }
149 // Read NRR data from memory
150 std::vector<u8> nrr_data(nrr_size);
151 Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size);
152 NRRHeader header;
153 std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
154
155 if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) {
156 LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic);
157 IPC::ResponseBuilder rb{ctx, 2};
158 rb.Push(ERROR_INVALID_NRR);
159 return;
160 }
161
162 if (header.size != nrr_size) {
163 LOG_ERROR(Service_LDR,
164 "NRR header reported size did not match LoadNrr parameter size! "
165 "(header_size={:016X}, loadnrr_size={:016X})",
166 header.size, nrr_size);
167 IPC::ResponseBuilder rb{ctx, 2};
168 rb.Push(ERROR_INVALID_SIZE);
169 return;
170 }
171
172 if (Core::CurrentProcess()->GetTitleID() != header.title_id) {
173 LOG_ERROR(Service_LDR,
174 "Attempting to load NRR with title ID other than current process. (actual "
175 "{:016X})!",
176 header.title_id);
177 IPC::ResponseBuilder rb{ctx, 2};
178 rb.Push(ERROR_INVALID_NRR);
179 return;
180 }
181
182 std::vector<SHA256Hash> hashes;
183
184 // Copy all hashes in the NRR (specified by hash count/hash offset) into vector.
185 for (std::size_t i = header.hash_offset;
186 i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) {
187 SHA256Hash hash;
188 std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash));
189 hashes.emplace_back(hash);
190 }
191
192 nrr.insert_or_assign(nrr_addr, std::move(hashes));
193
194 IPC::ResponseBuilder rb{ctx, 2};
195 rb.Push(RESULT_SUCCESS);
196 }
197
198 void UnloadNrr(Kernel::HLERequestContext& ctx) {
199 if (!initialized) {
200 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
201 IPC::ResponseBuilder rb{ctx, 2};
202 rb.Push(ERROR_NOT_INITIALIZED);
203 return;
204 }
205
206 IPC::RequestParser rp{ctx};
207 rp.Skip(2, false);
208 const auto nrr_addr{rp.Pop<VAddr>()};
209
210 if (!Common::Is4KBAligned(nrr_addr)) {
211 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
212 IPC::ResponseBuilder rb{ctx, 2};
213 rb.Push(ERROR_INVALID_ALIGNMENT);
214 return;
215 }
216
217 const auto iter = nrr.find(nrr_addr);
218 if (iter == nrr.end()) {
219 LOG_ERROR(Service_LDR,
220 "Attempting to unload NRR which has not been loaded! (addr={:016X})",
221 nrr_addr);
222 IPC::ResponseBuilder rb{ctx, 2};
223 rb.Push(ERROR_INVALID_NRR_ADDRESS);
224 return;
225 }
226
227 nrr.erase(iter);
78 IPC::ResponseBuilder rb{ctx, 2}; 228 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(RESULT_SUCCESS); 229 rb.Push(RESULT_SUCCESS);
80 LOG_WARNING(Service_LDR, "(STUBBED) called");
81 } 230 }
82 231
83 void LoadNro(Kernel::HLERequestContext& ctx) { 232 void LoadNro(Kernel::HLERequestContext& ctx) {
@@ -88,33 +237,253 @@ public:
88 const VAddr bss_addr{rp.Pop<VAddr>()}; 237 const VAddr bss_addr{rp.Pop<VAddr>()};
89 const u64 bss_size{rp.Pop<u64>()}; 238 const u64 bss_size{rp.Pop<u64>()};
90 239
240 if (!initialized) {
241 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
242 IPC::ResponseBuilder rb{ctx, 2};
243 rb.Push(ERROR_NOT_INITIALIZED);
244 return;
245 }
246
247 if (nro.size() >= MAXIMUM_LOADED_RO) {
248 LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs "
249 "(0x40)! Failing...");
250 IPC::ResponseBuilder rb{ctx, 2};
251 rb.Push(ERROR_MAXIMUM_NRO);
252 return;
253 }
254
255 // NRO Address does not fall on 0x1000 byte boundary
256 if (!Common::Is4KBAligned(nro_addr)) {
257 LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr);
258 IPC::ResponseBuilder rb{ctx, 2};
259 rb.Push(ERROR_INVALID_ALIGNMENT);
260 return;
261 }
262
263 // NRO Size or BSS Size is zero or causes overflow
264 const auto nro_size_valid =
265 nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size);
266 const auto bss_size_valid =
267 nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr);
268
269 if (!nro_size_valid || !bss_size_valid) {
270 LOG_ERROR(Service_LDR,
271 "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
272 "bss_address={:016X}, bss_size={:016X})",
273 nro_addr, nro_size, bss_addr, bss_size);
274 IPC::ResponseBuilder rb{ctx, 2};
275 rb.Push(ERROR_INVALID_SIZE);
276 return;
277 }
278
91 // Read NRO data from memory 279 // Read NRO data from memory
92 std::vector<u8> nro_data(nro_size); 280 std::vector<u8> nro_data(nro_size);
93 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); 281 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
94 282
283 SHA256Hash hash{};
284 mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
285
286 // NRO Hash is already loaded
287 if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) {
288 return info.second.hash == hash;
289 })) {
290 LOG_ERROR(Service_LDR, "NRO is already loaded!");
291 IPC::ResponseBuilder rb{ctx, 2};
292 rb.Push(ERROR_ALREADY_LOADED);
293 return;
294 }
295
296 // NRO Hash is not in any loaded NRR
297 if (!IsValidNROHash(hash)) {
298 LOG_ERROR(Service_LDR,
299 "NRO hash is not present in any currently loaded NRRs (hash={})!",
300 Common::HexArrayToString(hash));
301 IPC::ResponseBuilder rb{ctx, 2};
302 rb.Push(ERROR_MISSING_NRR_HASH);
303 return;
304 }
305
306 NROHeader header;
307 std::memcpy(&header, nro_data.data(), sizeof(NROHeader));
308
309 if (!IsValidNRO(header, nro_size, bss_size)) {
310 LOG_ERROR(Service_LDR, "NRO was invalid!");
311 IPC::ResponseBuilder rb{ctx, 2};
312 rb.Push(ERROR_INVALID_NRO);
313 return;
314 }
315
95 // Load NRO as new executable module 316 // Load NRO as new executable module
96 const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; 317 auto* process = Core::CurrentProcess();
97 Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); 318 auto& vm_manager = process->VMManager();
319 auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
320
321 if (!map_address.Succeeded() ||
322 *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) {
323
324 LOG_ERROR(Service_LDR,
325 "General error while allocation memory or no available memory to allocate!");
326 IPC::ResponseBuilder rb{ctx, 2};
327 rb.Push(ERROR_INVALID_MEMORY_STATE);
328 return;
329 }
330
331 ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size,
332 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
333 ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS);
334
335 if (bss_size > 0) {
336 ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
337 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
338 ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS);
339 }
340
341 vm_manager.ReprotectRange(*map_address, header.text_size,
342 Kernel::VMAPermission::ReadExecute);
343 vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size,
344 Kernel::VMAPermission::Read);
345 vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
346 Kernel::VMAPermission::ReadWrite);
98 347
99 // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. 348 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
100 // It is currently missing: 349 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
101 // - Signature checks with LoadNRR 350 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
102 // - Checking if a module has already been loaded 351 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
103 // - Using/validating BSS, etc. params (these are used from NRO header instead) 352
104 // - Error checking 353 nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size});
105 // - ...Probably other things
106 354
107 IPC::ResponseBuilder rb{ctx, 4}; 355 IPC::ResponseBuilder rb{ctx, 4};
108 rb.Push(RESULT_SUCCESS); 356 rb.Push(RESULT_SUCCESS);
109 rb.Push(addr); 357 rb.Push(*map_address);
110 LOG_WARNING(Service_LDR, "(STUBBED) called"); 358 }
359
360 void UnloadNro(Kernel::HLERequestContext& ctx) {
361 IPC::RequestParser rp{ctx};
362 rp.Skip(2, false);
363 const VAddr mapped_addr{rp.PopRaw<VAddr>()};
364 const VAddr heap_addr{rp.PopRaw<VAddr>()};
365
366 if (!initialized) {
367 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
368 IPC::ResponseBuilder rb{ctx, 2};
369 rb.Push(ERROR_NOT_INITIALIZED);
370 return;
371 }
372
373 if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) {
374 LOG_ERROR(Service_LDR,
375 "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, "
376 "bss_addr={:016X})!",
377 mapped_addr, heap_addr);
378 IPC::ResponseBuilder rb{ctx, 2};
379 rb.Push(ERROR_INVALID_ALIGNMENT);
380 return;
381 }
382
383 const auto iter = nro.find(mapped_addr);
384 if (iter == nro.end()) {
385 LOG_ERROR(Service_LDR,
386 "The NRO attempting to unmap was not mapped or has an invalid address "
387 "(actual {:016X})!",
388 mapped_addr);
389 IPC::ResponseBuilder rb{ctx, 2};
390 rb.Push(ERROR_INVALID_NRO_ADDRESS);
391 return;
392 }
393
394 auto* process = Core::CurrentProcess();
395 auto& vm_manager = process->VMManager();
396 const auto& nro_size = iter->second.size;
397
398 ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size,
399 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
400 ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
401
402 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
403 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
404 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
405 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
406
407 nro.erase(iter);
408 IPC::ResponseBuilder rb{ctx, 2};
409 rb.Push(RESULT_SUCCESS);
111 } 410 }
112 411
113 void Initialize(Kernel::HLERequestContext& ctx) { 412 void Initialize(Kernel::HLERequestContext& ctx) {
413 initialized = true;
414
114 IPC::ResponseBuilder rb{ctx, 2}; 415 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(RESULT_SUCCESS); 416 rb.Push(RESULT_SUCCESS);
116 LOG_WARNING(Service_LDR, "(STUBBED) called"); 417 LOG_WARNING(Service_LDR, "(STUBBED) called");
117 } 418 }
419
420private:
421 using SHA256Hash = std::array<u8, 0x20>;
422
423 struct NROHeader {
424 u32_le entrypoint_insn;
425 u32_le mod_offset;
426 INSERT_PADDING_WORDS(2);
427 u32_le magic;
428 INSERT_PADDING_WORDS(1);
429 u32_le nro_size;
430 INSERT_PADDING_WORDS(1);
431 u32_le text_offset;
432 u32_le text_size;
433 u32_le ro_offset;
434 u32_le ro_size;
435 u32_le rw_offset;
436 u32_le rw_size;
437 u32_le bss_size;
438 INSERT_PADDING_WORDS(1);
439 std::array<u8, 0x20> build_id;
440 INSERT_PADDING_BYTES(0x20);
441 };
442 static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
443
444 struct NRRHeader {
445 u32_le magic;
446 INSERT_PADDING_BYTES(0x1C);
447 u64_le title_id_mask;
448 u64_le title_id_pattern;
449 std::array<u8, 0x100> modulus;
450 std::array<u8, 0x100> signature_1;
451 std::array<u8, 0x100> signature_2;
452 u64_le title_id;
453 u32_le size;
454 INSERT_PADDING_BYTES(4);
455 u32_le hash_offset;
456 u32_le hash_count;
457 INSERT_PADDING_BYTES(8);
458 };
459 static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size.");
460
461 struct NROInfo {
462 SHA256Hash hash;
463 u64 size;
464 };
465
466 bool initialized = false;
467
468 std::map<VAddr, NROInfo> nro;
469 std::map<VAddr, std::vector<SHA256Hash>> nrr;
470
471 bool IsValidNROHash(const SHA256Hash& hash) {
472 return std::any_of(
473 nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) {
474 return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
475 });
476 }
477
478 static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
479 return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
480 header.nro_size == nro_size && header.bss_size == bss_size &&
481 header.ro_offset == header.text_offset + header.text_size &&
482 header.rw_offset == header.ro_offset + header.ro_size &&
483 nro_size == header.rw_offset + header.rw_size &&
484 Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
485 Common::Is4KBAligned(header.rw_size);
486 }
118}; 487};
119 488
120void InstallInterfaces(SM::ServiceManager& sm) { 489void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 44accecb7..1066bf505 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -351,6 +351,14 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
351 font_sizes.push_back(region.size); 351 font_sizes.push_back(region.size);
352 } 352 }
353 353
354 // Resize buffers if game requests smaller size output.
355 font_codes.resize(
356 std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32)));
357 font_offsets.resize(
358 std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32)));
359 font_sizes.resize(
360 std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32)));
361
354 ctx.WriteBuffer(font_codes, 0); 362 ctx.WriteBuffer(font_codes, 0);
355 ctx.WriteBuffer(font_offsets, 1); 363 ctx.WriteBuffer(font_offsets, 1);
356 ctx.WriteBuffer(font_sizes, 2); 364 ctx.WriteBuffer(font_sizes, 2);
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index e3cbd7004..b3a196f65 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -23,7 +23,8 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, 23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, 24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, 25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
26 {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, 26 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser,
27 "CalculateStandardUserSystemClockDifferenceByUser"},
27 {501, nullptr, "CalculateSpanBetween"}, 28 {501, nullptr, "CalculateSpanBetween"},
28 }; 29 };
29 RegisterHandlers(functions); 30 RegisterHandlers(functions);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 85e7b1195..e561a0c52 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -299,6 +299,21 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
299 ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot)); 299 ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
300} 300}
301 301
302void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
303 Kernel::HLERequestContext& ctx) {
304 LOG_DEBUG(Service_Time, "called");
305
306 IPC::RequestParser rp{ctx};
307 const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
308 const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
309 const u64 difference =
310 snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
311
312 IPC::ResponseBuilder rb{ctx, 4};
313 rb.Push(RESULT_SUCCESS);
314 rb.PushRaw<u64>(difference);
315}
316
302Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) 317Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
303 : ServiceFramework(name), time(std::move(time)) {} 318 : ServiceFramework(name), time(std::move(time)) {}
304 319
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 77871ae07..ea43fbea7 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -84,6 +84,7 @@ public:
84 void GetTimeZoneService(Kernel::HLERequestContext& ctx); 84 void GetTimeZoneService(Kernel::HLERequestContext& ctx);
85 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); 85 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
86 void GetClockSnapshot(Kernel::HLERequestContext& ctx); 86 void GetClockSnapshot(Kernel::HLERequestContext& ctx);
87 void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
87 88
88 protected: 89 protected:
89 std::shared_ptr<Module> time; 90 std::shared_ptr<Module> time;
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index d764b2406..d25fdb1fe 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -237,6 +237,22 @@ private:
237 Data data{}; 237 Data data{};
238}; 238};
239 239
240/// Represents a parcel containing one int '0' as its data
241/// Used by DetachBuffer and Disconnect
242class IGBPEmptyResponseParcel : public Parcel {
243protected:
244 void SerializeData() override {
245 Write(data);
246 }
247
248private:
249 struct Data {
250 u32_le unk_0;
251 };
252
253 Data data{};
254};
255
240class IGBPSetPreallocatedBufferRequestParcel : public Parcel { 256class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
241public: 257public:
242 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer) 258 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
@@ -554,6 +570,12 @@ private:
554 ctx.WriteBuffer(response.Serialize()); 570 ctx.WriteBuffer(response.Serialize());
555 } else if (transaction == TransactionId::CancelBuffer) { 571 } else if (transaction == TransactionId::CancelBuffer) {
556 LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); 572 LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
573 } else if (transaction == TransactionId::Disconnect ||
574 transaction == TransactionId::DetachBuffer) {
575 const auto buffer = ctx.ReadBuffer();
576
577 IGBPEmptyResponseParcel response{};
578 ctx.WriteBuffer(response.Serialize());
557 } else { 579 } else {
558 ASSERT_MSG(false, "Unimplemented"); 580 ASSERT_MSG(false, "Unimplemented");
559 } 581 }
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index c8e491fec..fbbd6b0de 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -170,17 +170,20 @@ static constexpr u32 PageAlignSize(u32 size) {
170 arg_data.size()); 170 arg_data.size());
171 } 171 }
172 172
173 // Read MOD header
174 ModHeader mod_header{};
175 // Default .bss to NRO header bss size if MOD0 section doesn't exist 173 // Default .bss to NRO header bss size if MOD0 section doesn't exist
176 u32 bss_size{PageAlignSize(nro_header.bss_size)}; 174 u32 bss_size{PageAlignSize(nro_header.bss_size)};
175
176 // Read MOD header
177 ModHeader mod_header{};
177 std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset, 178 std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
178 sizeof(ModHeader)); 179 sizeof(ModHeader));
180
179 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; 181 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
180 if (has_mod_header) { 182 if (has_mod_header) {
181 // Resize program image to include .bss section and page align each section 183 // Resize program image to include .bss section and page align each section
182 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 184 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
183 } 185 }
186
184 codeset.DataSegment().size += bss_size; 187 codeset.DataSegment().size += bss_size;
185 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 188 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
186 189
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 6de07ea56..a04e00ecb 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -34,8 +34,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is 34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
35 // needed for ARMS. 35 // needed for ARMS.
36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { 36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
37 regs.viewport[viewport].depth_range_near = 0.0f; 37 regs.viewports[viewport].depth_range_near = 0.0f;
38 regs.viewport[viewport].depth_range_far = 1.0f; 38 regs.viewports[viewport].depth_range_far = 1.0f;
39 } 39 }
40 // Doom and Bomberman seems to use the uninitialized registers and just enable blend 40 // Doom and Bomberman seems to use the uninitialized registers and just enable blend
41 // so initialize blend registers with sane values 41 // so initialize blend registers with sane values
@@ -66,6 +66,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
66 regs.stencil_back_func_func = Regs::ComparisonOp::Always; 66 regs.stencil_back_func_func = Regs::ComparisonOp::Always;
67 regs.stencil_back_func_mask = 0xFFFFFFFF; 67 regs.stencil_back_func_mask = 0xFFFFFFFF;
68 regs.stencil_back_mask = 0xFFFFFFFF; 68 regs.stencil_back_mask = 0xFFFFFFFF;
69 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
70 // register carrying a default value. Assume it's OpenGL's default (1).
71 regs.point_size = 1.0f;
69} 72}
70 73
71void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 74void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 91ca57883..9e480dc39 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -480,6 +480,67 @@ public:
480 }; 480 };
481 }; 481 };
482 482
483 struct ViewportTransform {
484 f32 scale_x;
485 f32 scale_y;
486 f32 scale_z;
487 f32 translate_x;
488 f32 translate_y;
489 f32 translate_z;
490 INSERT_PADDING_WORDS(2);
491
492 MathUtil::Rectangle<s32> GetRect() const {
493 return {
494 GetX(), // left
495 GetY() + GetHeight(), // top
496 GetX() + GetWidth(), // right
497 GetY() // bottom
498 };
499 };
500
501 s32 GetX() const {
502 return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
503 }
504
505 s32 GetY() const {
506 return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
507 }
508
509 s32 GetWidth() const {
510 return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
511 }
512
513 s32 GetHeight() const {
514 return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
515 }
516 };
517
518 struct ScissorTest {
519 u32 enable;
520 union {
521 BitField<0, 16, u32> min_x;
522 BitField<16, 16, u32> max_x;
523 };
524 union {
525 BitField<0, 16, u32> min_y;
526 BitField<16, 16, u32> max_y;
527 };
528 u32 fill;
529 };
530
531 struct ViewPort {
532 union {
533 BitField<0, 16, u32> x;
534 BitField<16, 16, u32> width;
535 };
536 union {
537 BitField<0, 16, u32> y;
538 BitField<16, 16, u32> height;
539 };
540 float depth_range_near;
541 float depth_range_far;
542 };
543
483 bool IsShaderConfigEnabled(std::size_t index) const { 544 bool IsShaderConfigEnabled(std::size_t index) const {
484 // The VertexB is always enabled. 545 // The VertexB is always enabled.
485 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) { 546 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
@@ -505,55 +566,11 @@ public:
505 566
506 INSERT_PADDING_WORDS(0x2E); 567 INSERT_PADDING_WORDS(0x2E);
507 568
508 RenderTargetConfig rt[NumRenderTargets]; 569 std::array<RenderTargetConfig, NumRenderTargets> rt;
509
510 struct {
511 f32 scale_x;
512 f32 scale_y;
513 f32 scale_z;
514 f32 translate_x;
515 f32 translate_y;
516 f32 translate_z;
517 INSERT_PADDING_WORDS(2);
518
519 MathUtil::Rectangle<s32> GetRect() const {
520 return {
521 GetX(), // left
522 GetY() + GetHeight(), // top
523 GetX() + GetWidth(), // right
524 GetY() // bottom
525 };
526 };
527
528 s32 GetX() const {
529 return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
530 }
531 570
532 s32 GetY() const { 571 std::array<ViewportTransform, NumViewports> viewport_transform;
533 return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
534 }
535 572
536 s32 GetWidth() const { 573 std::array<ViewPort, NumViewports> viewports;
537 return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
538 }
539
540 s32 GetHeight() const {
541 return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
542 }
543 } viewport_transform[NumViewports];
544
545 struct {
546 union {
547 BitField<0, 16, u32> x;
548 BitField<16, 16, u32> width;
549 };
550 union {
551 BitField<0, 16, u32> y;
552 BitField<16, 16, u32> height;
553 };
554 float depth_range_near;
555 float depth_range_far;
556 } viewport[NumViewports];
557 574
558 INSERT_PADDING_WORDS(0x1D); 575 INSERT_PADDING_WORDS(0x1D);
559 576
@@ -571,19 +588,9 @@ public:
571 588
572 INSERT_PADDING_WORDS(0x17); 589 INSERT_PADDING_WORDS(0x17);
573 590
574 struct { 591 std::array<ScissorTest, NumViewports> scissor_test;
575 u32 enable;
576 union {
577 BitField<0, 16, u32> min_x;
578 BitField<16, 16, u32> max_x;
579 };
580 union {
581 BitField<0, 16, u32> min_y;
582 BitField<16, 16, u32> max_y;
583 };
584 } scissor_test;
585 592
586 INSERT_PADDING_WORDS(0x52); 593 INSERT_PADDING_WORDS(0x15);
587 594
588 s32 stencil_back_func_ref; 595 s32 stencil_back_func_ref;
589 u32 stencil_back_mask; 596 u32 stencil_back_mask;
@@ -700,7 +707,9 @@ public:
700 u32 stencil_front_func_mask; 707 u32 stencil_front_func_mask;
701 u32 stencil_front_mask; 708 u32 stencil_front_mask;
702 709
703 INSERT_PADDING_WORDS(0x3); 710 INSERT_PADDING_WORDS(0x2);
711
712 u32 frag_color_clamp;
704 713
705 union { 714 union {
706 BitField<4, 1, u32> triangle_rast_flip; 715 BitField<4, 1, u32> triangle_rast_flip;
@@ -718,7 +727,12 @@ public:
718 727
719 u32 zeta_enable; 728 u32 zeta_enable;
720 729
721 INSERT_PADDING_WORDS(0x8); 730 union {
731 BitField<0, 1, u32> alpha_to_coverage;
732 BitField<4, 1, u32> alpha_to_one;
733 } multisample_control;
734
735 INSERT_PADDING_WORDS(0x7);
722 736
723 struct { 737 struct {
724 u32 tsc_address_high; 738 u32 tsc_address_high;
@@ -1100,8 +1114,8 @@ private:
1100ASSERT_REG_POSITION(macros, 0x45); 1114ASSERT_REG_POSITION(macros, 0x45);
1101ASSERT_REG_POSITION(tfb_enabled, 0x1D1); 1115ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
1102ASSERT_REG_POSITION(rt, 0x200); 1116ASSERT_REG_POSITION(rt, 0x200);
1103ASSERT_REG_POSITION(viewport_transform[0], 0x280); 1117ASSERT_REG_POSITION(viewport_transform, 0x280);
1104ASSERT_REG_POSITION(viewport, 0x300); 1118ASSERT_REG_POSITION(viewports, 0x300);
1105ASSERT_REG_POSITION(vertex_buffer, 0x35D); 1119ASSERT_REG_POSITION(vertex_buffer, 0x35D);
1106ASSERT_REG_POSITION(clear_color[0], 0x360); 1120ASSERT_REG_POSITION(clear_color[0], 0x360);
1107ASSERT_REG_POSITION(clear_depth, 0x364); 1121ASSERT_REG_POSITION(clear_depth, 0x364);
@@ -1136,10 +1150,12 @@ ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4);
1136ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5); 1150ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5);
1137ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); 1151ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
1138ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); 1152ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
1153ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
1139ASSERT_REG_POSITION(screen_y_control, 0x4EB); 1154ASSERT_REG_POSITION(screen_y_control, 0x4EB);
1140ASSERT_REG_POSITION(vb_element_base, 0x50D); 1155ASSERT_REG_POSITION(vb_element_base, 0x50D);
1141ASSERT_REG_POSITION(point_size, 0x546); 1156ASSERT_REG_POSITION(point_size, 0x546);
1142ASSERT_REG_POSITION(zeta_enable, 0x54E); 1157ASSERT_REG_POSITION(zeta_enable, 0x54E);
1158ASSERT_REG_POSITION(multisample_control, 0x54F);
1143ASSERT_REG_POSITION(tsc, 0x557); 1159ASSERT_REG_POSITION(tsc, 0x557);
1144ASSERT_REG_POSITION(tic, 0x55D); 1160ASSERT_REG_POSITION(tic, 0x55D);
1145ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); 1161ASSERT_REG_POSITION(stencil_two_side_enable, 0x565);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 54cc47a9b..ae6aaee4c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -107,8 +107,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
107 107
108 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); 108 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
109 OpenGLState::ApplyDefaultState(); 109 OpenGLState::ApplyDefaultState();
110 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
111 state.clip_distance[0] = true;
112 110
113 // Create render framebuffer 111 // Create render framebuffer
114 framebuffer.Create(); 112 framebuffer.Create();
@@ -582,6 +580,8 @@ void RasterizerOpenGL::DrawArrays() {
582 580
583 ConfigureFramebuffers(state); 581 ConfigureFramebuffers(state);
584 SyncColorMask(); 582 SyncColorMask();
583 SyncFragmentColorClampState();
584 SyncMultiSampleState();
585 SyncDepthTestState(); 585 SyncDepthTestState();
586 SyncStencilTestState(); 586 SyncStencilTestState();
587 SyncBlendState(); 587 SyncBlendState();
@@ -642,7 +642,7 @@ void RasterizerOpenGL::DrawArrays() {
642 params.DispatchDraw(); 642 params.DispatchDraw();
643 643
644 // Disable scissor test 644 // Disable scissor test
645 state.scissor.enabled = false; 645 state.viewports[0].scissor.enabled = false;
646 646
647 accelerate_draw = AccelDraw::Disabled; 647 accelerate_draw = AccelDraw::Disabled;
648 648
@@ -733,9 +733,8 @@ void RasterizerOpenGL::SamplerInfo::Create() {
733 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); 733 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
734} 734}
735 735
736void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) { 736void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
737 const GLuint s = sampler.handle; 737 const GLuint s = sampler.handle;
738 const Tegra::Texture::TSCEntry& config = info.tsc;
739 if (mag_filter != config.mag_filter) { 738 if (mag_filter != config.mag_filter) {
740 mag_filter = config.mag_filter; 739 mag_filter = config.mag_filter;
741 glSamplerParameteri( 740 glSamplerParameteri(
@@ -777,30 +776,50 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTex
777 MaxwellToGL::DepthCompareFunc(depth_compare_func)); 776 MaxwellToGL::DepthCompareFunc(depth_compare_func));
778 } 777 }
779 778
780 if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border || 779 GLvec4 new_border_color;
781 wrap_p == Tegra::Texture::WrapMode::Border) { 780 if (config.srgb_conversion) {
782 const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, 781 new_border_color[0] = config.srgb_border_color_r / 255.0f;
783 config.border_color_b, config.border_color_a}}; 782 new_border_color[1] = config.srgb_border_color_g / 255.0f;
784 if (border_color != new_border_color) { 783 new_border_color[2] = config.srgb_border_color_g / 255.0f;
785 border_color = new_border_color; 784 } else {
786 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data()); 785 new_border_color[0] = config.border_color_r;
787 } 786 new_border_color[1] = config.border_color_g;
787 new_border_color[2] = config.border_color_b;
788 } 788 }
789 if (info.tic.use_header_opt_control == 0) { 789 new_border_color[3] = config.border_color_a;
790
791 if (border_color != new_border_color) {
792 border_color = new_border_color;
793 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
794 }
795
796 const float anisotropic_max = static_cast<float>(1 << config.max_anisotropy.Value());
797 if (anisotropic_max != max_anisotropic) {
798 max_anisotropic = anisotropic_max;
790 if (GLAD_GL_ARB_texture_filter_anisotropic) { 799 if (GLAD_GL_ARB_texture_filter_anisotropic) {
791 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, 800 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic);
792 static_cast<float>(1 << info.tic.max_anisotropy.Value()));
793 } else if (GLAD_GL_EXT_texture_filter_anisotropic) { 801 } else if (GLAD_GL_EXT_texture_filter_anisotropic) {
794 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, 802 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic);
795 static_cast<float>(1 << info.tic.max_anisotropy.Value()));
796 } 803 }
797 glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, 804 }
798 static_cast<float>(info.tic.res_min_mip_level.Value())); 805 const float lod_min = static_cast<float>(config.min_lod_clamp.Value()) / 256.0f;
799 glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, 806 if (lod_min != min_lod) {
800 static_cast<float>(info.tic.res_max_mip_level.Value() == 0 807 min_lod = lod_min;
801 ? 16 808 glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, min_lod);
802 : info.tic.res_max_mip_level.Value())); 809 }
803 glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f); 810
811 const float lod_max = static_cast<float>(config.max_lod_clamp.Value()) / 256.0f;
812 if (lod_max != max_lod) {
813 max_lod = lod_max;
814 glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, max_lod);
815 }
816 const u32 bias = config.mip_lod_bias.Value();
817 // Sign extend the 13-bit value.
818 const u32 mask = 1U << (13 - 1);
819 const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f;
820 if (lod_bias != bias_lod) {
821 lod_bias = bias_lod;
822 glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias);
804 } 823 }
805} 824}
806 825
@@ -899,7 +918,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
899 continue; 918 continue;
900 } 919 }
901 920
902 texture_samplers[current_bindpoint].SyncWithConfig(texture); 921 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
903 Surface surface = res_cache.GetTextureSurface(texture, entry); 922 Surface surface = res_cache.GetTextureSurface(texture, entry);
904 if (surface != nullptr) { 923 if (surface != nullptr) {
905 state.texture_units[current_bindpoint].texture = surface->Texture().handle; 924 state.texture_units[current_bindpoint].texture = surface->Texture().handle;
@@ -923,15 +942,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
923 942
924void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { 943void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
925 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 944 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
926 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 945 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
927 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; 946 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
928 auto& viewport = current_state.viewports[i]; 947 auto& viewport = current_state.viewports[i];
929 viewport.x = viewport_rect.left; 948 viewport.x = viewport_rect.left;
930 viewport.y = viewport_rect.bottom; 949 viewport.y = viewport_rect.bottom;
931 viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth()); 950 viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth());
932 viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight()); 951 viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight());
933 viewport.depth_range_far = regs.viewport[i].depth_range_far; 952 viewport.depth_range_far = regs.viewports[i].depth_range_far;
934 viewport.depth_range_near = regs.viewport[i].depth_range_near; 953 viewport.depth_range_near = regs.viewports[i].depth_range_near;
935 } 954 }
936} 955}
937 956
@@ -1022,7 +1041,9 @@ void RasterizerOpenGL::SyncStencilTestState() {
1022 1041
1023void RasterizerOpenGL::SyncColorMask() { 1042void RasterizerOpenGL::SyncColorMask() {
1024 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1043 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1025 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 1044 const std::size_t count =
1045 regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
1046 for (std::size_t i = 0; i < count; i++) {
1026 const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i]; 1047 const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i];
1027 auto& dest = state.color_mask[i]; 1048 auto& dest = state.color_mask[i];
1028 dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE; 1049 dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
@@ -1032,6 +1053,17 @@ void RasterizerOpenGL::SyncColorMask() {
1032 } 1053 }
1033} 1054}
1034 1055
1056void RasterizerOpenGL::SyncMultiSampleState() {
1057 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1058 state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
1059 state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
1060}
1061
1062void RasterizerOpenGL::SyncFragmentColorClampState() {
1063 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1064 state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
1065}
1066
1035void RasterizerOpenGL::SyncBlendState() { 1067void RasterizerOpenGL::SyncBlendState() {
1036 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1068 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1037 1069
@@ -1043,43 +1075,40 @@ void RasterizerOpenGL::SyncBlendState() {
1043 state.independant_blend.enabled = regs.independent_blend_enable; 1075 state.independant_blend.enabled = regs.independent_blend_enable;
1044 if (!state.independant_blend.enabled) { 1076 if (!state.independant_blend.enabled) {
1045 auto& blend = state.blend[0]; 1077 auto& blend = state.blend[0];
1046 blend.enabled = regs.blend.enable[0] != 0; 1078 const auto& src = regs.blend;
1047 blend.separate_alpha = regs.blend.separate_alpha; 1079 blend.enabled = src.enable[0] != 0;
1048 blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb); 1080 if (blend.enabled) {
1049 blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb); 1081 blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
1050 blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb); 1082 blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
1051 if (blend.separate_alpha) { 1083 blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
1052 blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a); 1084 blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
1053 blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a); 1085 blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
1054 blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a); 1086 blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
1055 } 1087 }
1056 for (size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 1088 for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1057 state.blend[i].enabled = false; 1089 state.blend[i].enabled = false;
1058 } 1090 }
1059 return; 1091 return;
1060 } 1092 }
1061 1093
1062 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 1094 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1063 auto& blend = state.blend[i]; 1095 auto& blend = state.blend[i];
1096 const auto& src = regs.independent_blend[i];
1064 blend.enabled = regs.blend.enable[i] != 0; 1097 blend.enabled = regs.blend.enable[i] != 0;
1065 if (!blend.enabled) 1098 if (!blend.enabled)
1066 continue; 1099 continue;
1067 blend.separate_alpha = regs.independent_blend[i].separate_alpha; 1100 blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
1068 blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_rgb); 1101 blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
1069 blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_rgb); 1102 blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
1070 blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_rgb); 1103 blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
1071 if (blend.separate_alpha) { 1104 blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
1072 blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_a); 1105 blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
1073 blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_a);
1074 blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_a);
1075 }
1076 } 1106 }
1077} 1107}
1078 1108
1079void RasterizerOpenGL::SyncLogicOpState() { 1109void RasterizerOpenGL::SyncLogicOpState() {
1080 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1110 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1081 1111
1082 // TODO(Subv): Support more than just render target 0.
1083 state.logic_op.enabled = regs.logic_op.enable != 0; 1112 state.logic_op.enabled = regs.logic_op.enable != 0;
1084 1113
1085 if (!state.logic_op.enabled) 1114 if (!state.logic_op.enabled)
@@ -1092,19 +1121,21 @@ void RasterizerOpenGL::SyncLogicOpState() {
1092} 1121}
1093 1122
1094void RasterizerOpenGL::SyncScissorTest() { 1123void RasterizerOpenGL::SyncScissorTest() {
1095 // TODO: what is the correct behavior here, a single scissor for all targets
1096 // or scissor disabled for the rest of the targets?
1097 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1124 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1098 state.scissor.enabled = (regs.scissor_test.enable != 0); 1125 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
1099 if (regs.scissor_test.enable == 0) { 1126 const auto& src = regs.scissor_test[i];
1100 return; 1127 auto& dst = state.viewports[i].scissor;
1128 dst.enabled = (src.enable != 0);
1129 if (dst.enabled == 0) {
1130 return;
1131 }
1132 const u32 width = src.max_x - src.min_x;
1133 const u32 height = src.max_y - src.min_y;
1134 dst.x = src.min_x;
1135 dst.y = src.min_y;
1136 dst.width = width;
1137 dst.height = height;
1101 } 1138 }
1102 const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
1103 const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
1104 state.scissor.x = regs.scissor_test.min_x;
1105 state.scissor.y = regs.scissor_test.min_y;
1106 state.scissor.width = width;
1107 state.scissor.height = height;
1108} 1139}
1109 1140
1110void RasterizerOpenGL::SyncTransformFeedback() { 1141void RasterizerOpenGL::SyncTransformFeedback() {
@@ -1118,11 +1149,7 @@ void RasterizerOpenGL::SyncTransformFeedback() {
1118 1149
1119void RasterizerOpenGL::SyncPointState() { 1150void RasterizerOpenGL::SyncPointState() {
1120 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1151 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1121 1152 state.point.size = regs.point_size;
1122 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
1123 // register carrying a default value. For now, if the point size is zero, assume it's
1124 // OpenGL's default (1).
1125 state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
1126} 1153}
1127 1154
1128void RasterizerOpenGL::CheckAlphaTests() { 1155void RasterizerOpenGL::CheckAlphaTests() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 8ef0f6c12..6e78ab4cd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -88,7 +88,7 @@ private:
88 /// SamplerInfo struct. 88 /// SamplerInfo struct.
89 void Create(); 89 void Create();
90 /// Syncs the sampler object with the config, updating any necessary state. 90 /// Syncs the sampler object with the config, updating any necessary state.
91 void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info); 91 void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
92 92
93 private: 93 private:
94 Tegra::Texture::TextureFilter mag_filter; 94 Tegra::Texture::TextureFilter mag_filter;
@@ -100,6 +100,10 @@ private:
100 bool uses_depth_compare; 100 bool uses_depth_compare;
101 Tegra::Texture::DepthCompareFunc depth_compare_func; 101 Tegra::Texture::DepthCompareFunc depth_compare_func;
102 GLvec4 border_color; 102 GLvec4 border_color;
103 float min_lod;
104 float max_lod;
105 float lod_bias;
106 float max_anisotropic;
103 }; 107 };
104 108
105 /** 109 /**
@@ -160,6 +164,12 @@ private:
160 /// Syncs the LogicOp state to match the guest state 164 /// Syncs the LogicOp state to match the guest state
161 void SyncLogicOpState(); 165 void SyncLogicOpState();
162 166
167 /// Syncs the the color clamp state
168 void SyncFragmentColorClampState();
169
170 /// Syncs the alpha coverage and alpha to one
171 void SyncMultiSampleState();
172
163 /// Syncs the scissor test state to match the guest state 173 /// Syncs the scissor test state to match the guest state
164 void SyncScissorTest(); 174 void SyncScissorTest();
165 175
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index b44ecfa1c..9ca82c06c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -381,11 +381,8 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d
381 const u32 tile_size_y{GetDefaultBlockHeight(format)}; 381 const u32 tile_size_y{GetDefaultBlockHeight(format)};
382 382
383 if (morton_to_gl) { 383 if (morton_to_gl) {
384 const std::vector<u8> data = 384 Tegra::Texture::UnswizzleTexture(gl_buffer, addr, tile_size_x, tile_size_y, bytes_per_pixel,
385 Tegra::Texture::UnswizzleTexture(addr, tile_size_x, tile_size_y, bytes_per_pixel, 385 stride, height, depth, block_height, block_depth);
386 stride, height, depth, block_height, block_depth);
387 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
388 memcpy(gl_buffer, data.data(), size_to_copy);
389 } else { 386 } else {
390 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x, 387 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
391 (height + tile_size_y - 1) / tile_size_y, depth, 388 (height + tile_size_y - 1) / tile_size_y, depth,
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 2a069cdd8..9a5d7e289 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -67,6 +67,7 @@ public:
67 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); 67 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs);
68 state.draw.shader_program = 0; 68 state.draw.shader_program = 0;
69 state.draw.program_pipeline = pipeline.handle; 69 state.draw.program_pipeline = pipeline.handle;
70 state.geometry_shaders.enabled = (gs != 0);
70 } 71 }
71 72
72private: 73private:
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 98622a058..d9910c6e8 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -14,7 +14,10 @@ OpenGLState OpenGLState::cur_state;
14bool OpenGLState::s_rgb_used; 14bool OpenGLState::s_rgb_used;
15OpenGLState::OpenGLState() { 15OpenGLState::OpenGLState() {
16 // These all match default OpenGL values 16 // These all match default OpenGL values
17 geometry_shaders.enabled = false;
17 framebuffer_srgb.enabled = false; 18 framebuffer_srgb.enabled = false;
19 multisample_control.alpha_to_coverage = false;
20 multisample_control.alpha_to_one = false;
18 cull.enabled = false; 21 cull.enabled = false;
19 cull.mode = GL_BACK; 22 cull.mode = GL_BACK;
20 cull.front_face = GL_CCW; 23 cull.front_face = GL_CCW;
@@ -50,12 +53,12 @@ OpenGLState::OpenGLState() {
50 item.height = 0; 53 item.height = 0;
51 item.depth_range_near = 0.0f; 54 item.depth_range_near = 0.0f;
52 item.depth_range_far = 1.0f; 55 item.depth_range_far = 1.0f;
56 item.scissor.enabled = false;
57 item.scissor.x = 0;
58 item.scissor.y = 0;
59 item.scissor.width = 0;
60 item.scissor.height = 0;
53 } 61 }
54 scissor.enabled = false;
55 scissor.x = 0;
56 scissor.y = 0;
57 scissor.width = 0;
58 scissor.height = 0;
59 for (auto& item : blend) { 62 for (auto& item : blend) {
60 item.enabled = true; 63 item.enabled = true;
61 item.rgb_equation = GL_FUNC_ADD; 64 item.rgb_equation = GL_FUNC_ADD;
@@ -88,6 +91,7 @@ OpenGLState::OpenGLState() {
88 clip_distance = {}; 91 clip_distance = {};
89 92
90 point.size = 1; 93 point.size = 1;
94 fragment_color_clamp.enabled = false;
91} 95}
92 96
93void OpenGLState::ApplyDefaultState() { 97void OpenGLState::ApplyDefaultState() {
@@ -136,7 +140,7 @@ void OpenGLState::ApplyCulling() const {
136} 140}
137 141
138void OpenGLState::ApplyColorMask() const { 142void OpenGLState::ApplyColorMask() const {
139 if (GLAD_GL_ARB_viewport_array) { 143 if (GLAD_GL_ARB_viewport_array && independant_blend.enabled) {
140 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 144 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
141 const auto& updated = color_mask[i]; 145 const auto& updated = color_mask[i];
142 const auto& current = cur_state.color_mask[i]; 146 const auto& current = cur_state.color_mask[i];
@@ -230,26 +234,10 @@ void OpenGLState::ApplyStencilTest() const {
230 } 234 }
231} 235}
232 236
233void OpenGLState::ApplyScissor() const {
234 const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
235 if (scissor_changed) {
236 if (scissor.enabled) {
237 glEnable(GL_SCISSOR_TEST);
238 } else {
239 glDisable(GL_SCISSOR_TEST);
240 }
241 }
242 if (scissor.enabled &&
243 (scissor_changed || scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
244 scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height)) {
245 glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
246 }
247}
248
249void OpenGLState::ApplyViewport() const { 237void OpenGLState::ApplyViewport() const {
250 if (GLAD_GL_ARB_viewport_array) { 238 if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) {
251 for (GLuint i = 0; 239 for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports);
252 i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); i++) { 240 i++) {
253 const auto& current = cur_state.viewports[i]; 241 const auto& current = cur_state.viewports[i];
254 const auto& updated = viewports[i]; 242 const auto& updated = viewports[i];
255 if (updated.x != current.x || updated.y != current.y || 243 if (updated.x != current.x || updated.y != current.y ||
@@ -260,6 +248,22 @@ void OpenGLState::ApplyViewport() const {
260 updated.depth_range_far != current.depth_range_far) { 248 updated.depth_range_far != current.depth_range_far) {
261 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far); 249 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
262 } 250 }
251 const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
252 if (scissor_changed) {
253 if (updated.scissor.enabled) {
254 glEnablei(GL_SCISSOR_TEST, i);
255 } else {
256 glDisablei(GL_SCISSOR_TEST, i);
257 }
258 }
259 if (updated.scissor.enabled &&
260 (scissor_changed || updated.scissor.x != current.scissor.x ||
261 updated.scissor.y != current.scissor.y ||
262 updated.scissor.width != current.scissor.width ||
263 updated.scissor.height != current.scissor.height)) {
264 glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
265 updated.scissor.height);
266 }
263 } 267 }
264 } else { 268 } else {
265 const auto& current = cur_state.viewports[0]; 269 const auto& current = cur_state.viewports[0];
@@ -273,6 +277,21 @@ void OpenGLState::ApplyViewport() const {
273 updated.depth_range_far != current.depth_range_far) { 277 updated.depth_range_far != current.depth_range_far) {
274 glDepthRange(updated.depth_range_near, updated.depth_range_far); 278 glDepthRange(updated.depth_range_near, updated.depth_range_far);
275 } 279 }
280 const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
281 if (scissor_changed) {
282 if (updated.scissor.enabled) {
283 glEnable(GL_SCISSOR_TEST);
284 } else {
285 glDisable(GL_SCISSOR_TEST);
286 }
287 }
288 if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x ||
289 updated.scissor.y != current.scissor.y ||
290 updated.scissor.width != current.scissor.width ||
291 updated.scissor.height != current.scissor.height)) {
292 glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width,
293 updated.scissor.height);
294 }
276 } 295 }
277} 296}
278 297
@@ -290,27 +309,16 @@ void OpenGLState::ApplyGlobalBlending() const {
290 if (!updated.enabled) { 309 if (!updated.enabled) {
291 return; 310 return;
292 } 311 }
293 if (updated.separate_alpha) { 312 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
294 if (blend_changed || updated.src_rgb_func != current.src_rgb_func || 313 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
295 updated.dst_rgb_func != current.dst_rgb_func || 314 updated.dst_a_func != current.dst_a_func) {
296 updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { 315 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
297 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, 316 updated.dst_a_func);
298 updated.dst_a_func); 317 }
299 }
300
301 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
302 updated.a_equation != current.a_equation) {
303 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
304 }
305 } else {
306 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
307 updated.dst_rgb_func != current.dst_rgb_func) {
308 glBlendFunc(updated.src_rgb_func, updated.dst_rgb_func);
309 }
310 318
311 if (blend_changed || updated.rgb_equation != current.rgb_equation) { 319 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
312 glBlendEquation(updated.rgb_equation); 320 updated.a_equation != current.a_equation) {
313 } 321 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
314 } 322 }
315} 323}
316 324
@@ -328,29 +336,17 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
328 if (!updated.enabled) { 336 if (!updated.enabled) {
329 return; 337 return;
330 } 338 }
331 if (updated.separate_alpha) { 339 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
332 if (blend_changed || updated.src_rgb_func != current.src_rgb_func || 340 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
333 updated.dst_rgb_func != current.dst_rgb_func || 341 updated.dst_a_func != current.dst_a_func) {
334 updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { 342 glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func,
335 glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func, 343 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
336 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); 344 }
337 }
338
339 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
340 updated.a_equation != current.a_equation) {
341 glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
342 updated.a_equation);
343 }
344 } else {
345 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
346 updated.dst_rgb_func != current.dst_rgb_func) {
347 glBlendFunciARB(static_cast<GLuint>(target), updated.src_rgb_func,
348 updated.dst_rgb_func);
349 }
350 345
351 if (blend_changed || updated.rgb_equation != current.rgb_equation) { 346 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
352 glBlendEquationiARB(static_cast<GLuint>(target), updated.rgb_equation); 347 updated.a_equation != current.a_equation) {
353 } 348 glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
349 updated.a_equation);
354 } 350 }
355} 351}
356 352
@@ -481,9 +477,29 @@ void OpenGLState::Apply() const {
481 if (point.size != cur_state.point.size) { 477 if (point.size != cur_state.point.size) {
482 glPointSize(point.size); 478 glPointSize(point.size);
483 } 479 }
480 if (GLAD_GL_ARB_color_buffer_float) {
481 if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) {
482 glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
483 fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
484 }
485 }
486 if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) {
487 if (multisample_control.alpha_to_coverage) {
488 glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
489 } else {
490 glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
491 }
492 }
493 if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) {
494 if (multisample_control.alpha_to_one) {
495 glEnable(GL_SAMPLE_ALPHA_TO_ONE);
496 } else {
497 glDisable(GL_SAMPLE_ALPHA_TO_ONE);
498 }
499 }
500
484 ApplyColorMask(); 501 ApplyColorMask();
485 ApplyViewport(); 502 ApplyViewport();
486 ApplyScissor();
487 ApplyStencilTest(); 503 ApplyStencilTest();
488 ApplySRgb(); 504 ApplySRgb();
489 ApplyCulling(); 505 ApplyCulling();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index e5d1baae6..bdc743b0f 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -40,6 +40,19 @@ public:
40 } framebuffer_srgb; 40 } framebuffer_srgb;
41 41
42 struct { 42 struct {
43 bool alpha_to_coverage; // GL_ALPHA_TO_COVERAGE
44 bool alpha_to_one; // GL_ALPHA_TO_ONE
45 } multisample_control;
46
47 struct {
48 bool enabled; // GL_CLAMP_FRAGMENT_COLOR_ARB
49 } fragment_color_clamp;
50
51 struct {
52 bool enabled; // viewports arrays are only supported when geometry shaders are enabled.
53 } geometry_shaders;
54
55 struct {
43 bool enabled; // GL_CULL_FACE 56 bool enabled; // GL_CULL_FACE
44 GLenum mode; // GL_CULL_FACE_MODE 57 GLenum mode; // GL_CULL_FACE_MODE
45 GLenum front_face; // GL_FRONT_FACE 58 GLenum front_face; // GL_FRONT_FACE
@@ -79,7 +92,6 @@ public:
79 92
80 struct Blend { 93 struct Blend {
81 bool enabled; // GL_BLEND 94 bool enabled; // GL_BLEND
82 bool separate_alpha; // Independent blend enabled
83 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB 95 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
84 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA 96 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
85 GLenum src_rgb_func; // GL_BLEND_SRC_RGB 97 GLenum src_rgb_func; // GL_BLEND_SRC_RGB
@@ -150,16 +162,15 @@ public:
150 GLfloat height; 162 GLfloat height;
151 GLfloat depth_range_near; // GL_DEPTH_RANGE 163 GLfloat depth_range_near; // GL_DEPTH_RANGE
152 GLfloat depth_range_far; // GL_DEPTH_RANGE 164 GLfloat depth_range_far; // GL_DEPTH_RANGE
165 struct {
166 bool enabled; // GL_SCISSOR_TEST
167 GLint x;
168 GLint y;
169 GLsizei width;
170 GLsizei height;
171 } scissor;
153 }; 172 };
154 std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> viewports; 173 std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
155
156 struct {
157 bool enabled; // GL_SCISSOR_TEST
158 GLint x;
159 GLint y;
160 GLsizei width;
161 GLsizei height;
162 } scissor;
163 174
164 struct { 175 struct {
165 float size; // GL_POINT_SIZE 176 float size; // GL_POINT_SIZE
@@ -214,7 +225,6 @@ private:
214 void ApplyLogicOp() const; 225 void ApplyLogicOp() const;
215 void ApplyTextures() const; 226 void ApplyTextures() const;
216 void ApplySamplers() const; 227 void ApplySamplers() const;
217 void ApplyScissor() const;
218}; 228};
219 229
220} // namespace OpenGL 230} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 3ce2cc6d2..065b3929c 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -180,6 +180,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
180 return GL_CLAMP_TO_BORDER; 180 return GL_CLAMP_TO_BORDER;
181 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: 181 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
182 return GL_MIRROR_CLAMP_TO_EDGE; 182 return GL_MIRROR_CLAMP_TO_EDGE;
183 case Tegra::Texture::WrapMode::MirrorOnceBorder:
184 if (GL_EXT_texture_mirror_clamp) {
185 return GL_MIRROR_CLAMP_TO_BORDER_EXT;
186 } else {
187 return GL_MIRROR_CLAMP_TO_EDGE;
188 }
183 } 189 }
184 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); 190 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
185 return GL_REPEAT; 191 return GL_REPEAT;
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 7040d9bf5..7eabd34f1 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -226,14 +226,21 @@ u32 BytesPerPixel(TextureFormat format) {
226 } 226 }
227} 227}
228 228
229void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
230 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height,
231 u32 block_depth) {
232 CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
233 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
234 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true,
235 block_height, block_depth);
236}
237
229std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 238std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
230 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 239 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
231 u32 block_height, u32 block_depth) { 240 u32 block_height, u32 block_depth) {
232 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); 241 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
233 CopySwizzledData((width + tile_size_x - 1) / tile_size_x, 242 UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel,
234 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel, 243 width, height, depth, block_height, block_depth);
235 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true,
236 block_height, block_depth);
237 return unswizzled_data; 244 return unswizzled_data;
238} 245}
239 246
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index ba065510b..f4ef7c73e 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -19,6 +19,13 @@ inline std::size_t GetGOBSize() {
19/** 19/**
20 * Unswizzles a swizzled texture without changing its format. 20 * Unswizzles a swizzled texture without changing its format.
21 */ 21 */
22void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
23 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight,
25 u32 block_depth = TICEntry::DefaultBlockHeight);
26/**
27 * Unswizzles a swizzled texture without changing its format.
28 */
22std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 29std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
23 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 30 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight, 31 u32 block_height = TICEntry::DefaultBlockHeight,
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index e199d019a..ffa08f5c1 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -190,6 +190,7 @@ struct TICEntry {
190 union { 190 union {
191 BitField<0, 4, u32> res_min_mip_level; 191 BitField<0, 4, u32> res_min_mip_level;
192 BitField<4, 4, u32> res_max_mip_level; 192 BitField<4, 4, u32> res_max_mip_level;
193 BitField<12, 12, u32> min_lod_clamp;
193 }; 194 };
194 195
195 GPUVAddr Address() const { 196 GPUVAddr Address() const {
@@ -284,13 +285,25 @@ struct TSCEntry {
284 BitField<6, 3, WrapMode> wrap_p; 285 BitField<6, 3, WrapMode> wrap_p;
285 BitField<9, 1, u32> depth_compare_enabled; 286 BitField<9, 1, u32> depth_compare_enabled;
286 BitField<10, 3, DepthCompareFunc> depth_compare_func; 287 BitField<10, 3, DepthCompareFunc> depth_compare_func;
288 BitField<13, 1, u32> srgb_conversion;
289 BitField<20, 3, u32> max_anisotropy;
287 }; 290 };
288 union { 291 union {
289 BitField<0, 2, TextureFilter> mag_filter; 292 BitField<0, 2, TextureFilter> mag_filter;
290 BitField<4, 2, TextureFilter> min_filter; 293 BitField<4, 2, TextureFilter> min_filter;
291 BitField<6, 2, TextureMipmapFilter> mip_filter; 294 BitField<6, 2, TextureMipmapFilter> mip_filter;
295 BitField<9, 1, u32> cubemap_interface_filtering;
296 BitField<12, 13, u32> mip_lod_bias;
297 };
298 union {
299 BitField<0, 12, u32> min_lod_clamp;
300 BitField<12, 12, u32> max_lod_clamp;
301 BitField<24, 8, u32> srgb_border_color_r;
302 };
303 union {
304 BitField<12, 8, u32> srgb_border_color_g;
305 BitField<20, 8, u32> srgb_border_color_b;
292 }; 306 };
293 INSERT_PADDING_BYTES(8);
294 float border_color_r; 307 float border_color_r;
295 float border_color_g; 308 float border_color_g;
296 float border_color_b; 309 float border_color_b;
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp
index 639d5df0f..ae8cac243 100644
--- a/src/yuzu/configuration/configure_gamelist.cpp
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -36,6 +36,16 @@ ConfigureGameList::ConfigureGameList(QWidget* parent)
36 InitializeRowComboBoxes(); 36 InitializeRowComboBoxes();
37 37
38 this->setConfiguration(); 38 this->setConfiguration();
39
40 // Force game list reload if any of the relevant settings are changed.
41 connect(ui->show_unknown, &QCheckBox::stateChanged, this,
42 &ConfigureGameList::RequestGameListUpdate);
43 connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
44 &ConfigureGameList::RequestGameListUpdate);
45 connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
46 &ConfigureGameList::RequestGameListUpdate);
47 connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
48 &ConfigureGameList::RequestGameListUpdate);
39} 49}
40 50
41ConfigureGameList::~ConfigureGameList() = default; 51ConfigureGameList::~ConfigureGameList() = default;
@@ -49,6 +59,10 @@ void ConfigureGameList::applyConfiguration() {
49 Settings::Apply(); 59 Settings::Apply();
50} 60}
51 61
62void ConfigureGameList::RequestGameListUpdate() {
63 UISettings::values.is_game_list_reload_pending.exchange(true);
64}
65
52void ConfigureGameList::setConfiguration() { 66void ConfigureGameList::setConfiguration() {
53 ui->show_unknown->setChecked(UISettings::values.show_unknown); 67 ui->show_unknown->setChecked(UISettings::values.show_unknown);
54 ui->show_add_ons->setChecked(UISettings::values.show_add_ons); 68 ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h
index ff7406c60..bbf7e25f1 100644
--- a/src/yuzu/configuration/configure_gamelist.h
+++ b/src/yuzu/configuration/configure_gamelist.h
@@ -21,6 +21,8 @@ public:
21 void applyConfiguration(); 21 void applyConfiguration();
22 22
23private: 23private:
24 void RequestGameListUpdate();
25
24 void setConfiguration(); 26 void setConfiguration();
25 27
26 void changeEvent(QEvent*) override; 28 void changeEvent(QEvent*) override;
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 314f51203..c22742007 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -23,6 +23,9 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
23 23
24 this->setConfiguration(); 24 this->setConfiguration();
25 25
26 connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this,
27 [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
28
26 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 29 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
27} 30}
28 31
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 131ad19de..4b969119c 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -12,7 +12,7 @@
12#include "core/file_sys/vfs_real.h" 12#include "core/file_sys/vfs_real.h"
13#include "core/hle/service/acc/profile_manager.h" 13#include "core/hle/service/acc/profile_manager.h"
14 14
15// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows 15// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
16// defines. 16// defines.
17static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper( 17static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
18 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) { 18 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
@@ -308,6 +308,8 @@ void GMainWindow::InitializeHotkeys() {
308 Qt::ApplicationShortcut); 308 Qt::ApplicationShortcut);
309 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"), 309 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
310 Qt::ApplicationShortcut); 310 Qt::ApplicationShortcut);
311 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
312 Qt::ApplicationShortcut);
311 hotkey_registry.LoadHotkeys(); 313 hotkey_registry.LoadHotkeys();
312 314
313 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, 315 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
@@ -361,6 +363,12 @@ void GMainWindow::InitializeHotkeys() {
361 UpdateStatusBar(); 363 UpdateStatusBar();
362 } 364 }
363 }); 365 });
366 connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated,
367 this, [&] {
368 if (ui.action_Load_Amiibo->isEnabled()) {
369 OnLoadAmiibo();
370 }
371 });
364} 372}
365 373
366void GMainWindow::SetDefaultUIGeometry() { 374void GMainWindow::SetDefaultUIGeometry() {
@@ -1333,7 +1341,13 @@ void GMainWindow::OnConfigure() {
1333 UpdateUITheme(); 1341 UpdateUITheme();
1334 if (UISettings::values.enable_discord_presence != old_discord_presence) 1342 if (UISettings::values.enable_discord_presence != old_discord_presence)
1335 SetDiscordEnabled(UISettings::values.enable_discord_presence); 1343 SetDiscordEnabled(UISettings::values.enable_discord_presence);
1336 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1344
1345 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
1346 if (reload) {
1347 game_list->PopulateAsync(UISettings::values.gamedir,
1348 UISettings::values.gamedir_deepscan);
1349 }
1350
1337 config->Save(); 1351 config->Save();
1338 } 1352 }
1339} 1353}
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 32a0d813c..e80aebc0a 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
8#include <vector> 9#include <vector>
9#include <QByteArray> 10#include <QByteArray>
10#include <QString> 11#include <QString>
@@ -63,6 +64,7 @@ struct Values {
63 uint32_t icon_size; 64 uint32_t icon_size;
64 uint8_t row_1_text_id; 65 uint8_t row_1_text_id;
65 uint8_t row_2_text_id; 66 uint8_t row_2_text_id;
67 std::atomic_bool is_game_list_reload_pending{false};
66}; 68};
67 69
68extern Values values; 70extern Values values;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 9cc409fd5..96f1ef636 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -139,6 +139,8 @@ void Config::ReadValues() {
139 Settings::values.rng_seed = std::nullopt; 139 Settings::values.rng_seed = std::nullopt;
140 } 140 }
141 141
142 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
143
142 // Miscellaneous 144 // Miscellaneous
143 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 145 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
144 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); 146 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);