summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/file_sys/system_archive/ng_word.cpp41
-rw-r--r--src/core/file_sys/system_archive/ng_word.h1
-rw-r--r--src/core/file_sys/system_archive/system_archive.cpp7
-rw-r--r--src/core/hle/kernel/object.cpp2
-rw-r--r--src/core/hle/kernel/process.cpp42
-rw-r--r--src/core/hle/kernel/process.h30
-rw-r--r--src/core/hle/kernel/readable_event.cpp12
-rw-r--r--src/core/hle/kernel/readable_event.h11
-rw-r--r--src/core/hle/kernel/svc.cpp15
-rw-r--r--src/core/hle/service/ldr/ldr.cpp10
-rw-r--r--src/core/memory.cpp8
-rw-r--r--src/video_core/engines/shader_bytecode.h3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp116
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp70
15 files changed, 292 insertions, 82 deletions
diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp
index d0acdbd49..f4443784d 100644
--- a/src/core/file_sys/system_archive/ng_word.cpp
+++ b/src/core/file_sys/system_archive/ng_word.cpp
@@ -26,7 +26,7 @@ constexpr std::array<u8, 30> WORD_TXT{
26VirtualDir NgWord1() { 26VirtualDir NgWord1() {
27 std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES); 27 std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES);
28 28
29 for (std::size_t i = 0; i < NgWord1Data::NUMBER_WORD_TXT_FILES; ++i) { 29 for (std::size_t i = 0; i < files.size(); ++i) {
30 files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( 30 files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>(
31 NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)); 31 NgWord1Data::WORD_TXT, fmt::format("{}.txt", i));
32 } 32 }
@@ -39,4 +39,43 @@ VirtualDir NgWord1() {
39 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); 39 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data");
40} 40}
41 41
42namespace NgWord2Data {
43
44constexpr std::size_t NUMBER_AC_NX_FILES = 0x10;
45
46// Should this archive replacement mysteriously not work on a future game, consider updating.
47constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x15}; // 5.1.0 System Version
48
49constexpr std::array<u8, 0x2C> AC_NX_DATA{
50 0x1F, 0x8B, 0x08, 0x08, 0xD5, 0x2C, 0x09, 0x5C, 0x04, 0x00, 0x61, 0x63, 0x72, 0x61, 0x77,
51 0x00, 0xED, 0xC1, 0x01, 0x0D, 0x00, 0x00, 0x00, 0xC2, 0x20, 0xFB, 0xA7, 0xB6, 0xC7, 0x07,
52 0x0C, 0x00, 0x00, 0x00, 0xC8, 0x3B, 0x11, 0x00, 0x1C, 0xC7, 0x00, 0x10, 0x00, 0x00,
53}; // Deserializes to no bad words
54
55} // namespace NgWord2Data
56
57VirtualDir NgWord2() {
58 std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3);
59
60 for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) {
61 files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
62 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i));
63 files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
64 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i));
65 files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
66 NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i));
67 }
68
69 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
70 NgWord2Data::AC_NX_DATA, "ac_common_b1_nx"));
71 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
72 NgWord2Data::AC_NX_DATA, "ac_common_b2_nx"));
73 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
74 NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
75 files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>(
76 NgWord2Data::VERSION_DAT, "version.dat"));
77
78 return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data");
79}
80
42} // namespace FileSys::SystemArchive 81} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/system_archive/ng_word.h b/src/core/file_sys/system_archive/ng_word.h
index f4bc67344..cd81e0abb 100644
--- a/src/core/file_sys/system_archive/ng_word.h
+++ b/src/core/file_sys/system_archive/ng_word.h
@@ -9,5 +9,6 @@
9namespace FileSys::SystemArchive { 9namespace FileSys::SystemArchive {
10 10
11VirtualDir NgWord1(); 11VirtualDir NgWord1();
12VirtualDir NgWord2();
12 13
13} // namespace FileSys::SystemArchive 14} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp
index c9c40a07d..e3e79f40a 100644
--- a/src/core/file_sys/system_archive/system_archive.cpp
+++ b/src/core/file_sys/system_archive/system_archive.cpp
@@ -2,7 +2,6 @@
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 <functional>
6#include "common/logging/log.h" 5#include "common/logging/log.h"
7#include "core/file_sys/romfs.h" 6#include "core/file_sys/romfs.h"
8#include "core/file_sys/system_archive/ng_word.h" 7#include "core/file_sys/system_archive/ng_word.h"
@@ -13,7 +12,7 @@ namespace FileSys::SystemArchive {
13constexpr u64 SYSTEM_ARCHIVE_BASE_TITLE_ID = 0x0100000000000800; 12constexpr u64 SYSTEM_ARCHIVE_BASE_TITLE_ID = 0x0100000000000800;
14constexpr std::size_t SYSTEM_ARCHIVE_COUNT = 0x28; 13constexpr std::size_t SYSTEM_ARCHIVE_COUNT = 0x28;
15 14
16using SystemArchiveSupplier = std::function<VirtualDir()>; 15using SystemArchiveSupplier = VirtualDir (*)();
17 16
18struct SystemArchiveDescriptor { 17struct SystemArchiveDescriptor {
19 u64 title_id; 18 u64 title_id;
@@ -21,7 +20,7 @@ struct SystemArchiveDescriptor {
21 SystemArchiveSupplier supplier; 20 SystemArchiveSupplier supplier;
22}; 21};
23 22
24const std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES = {{ 23constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES{{
25 {0x0100000000000800, "CertStore", nullptr}, 24 {0x0100000000000800, "CertStore", nullptr},
26 {0x0100000000000801, "ErrorMessage", nullptr}, 25 {0x0100000000000801, "ErrorMessage", nullptr},
27 {0x0100000000000802, "MiiModel", nullptr}, 26 {0x0100000000000802, "MiiModel", nullptr},
@@ -57,7 +56,7 @@ const std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES
57 {0x0100000000000820, "PlatformConfigCopper", nullptr}, 56 {0x0100000000000820, "PlatformConfigCopper", nullptr},
58 {0x0100000000000821, "PlatformConfigHoag", nullptr}, 57 {0x0100000000000821, "PlatformConfigHoag", nullptr},
59 {0x0100000000000822, "ControllerFirmware", nullptr}, 58 {0x0100000000000822, "ControllerFirmware", nullptr},
60 {0x0100000000000823, "NgWord2", nullptr}, 59 {0x0100000000000823, "NgWord2", &NgWord2},
61 {0x0100000000000824, "PlatformConfigIcosaMariko", nullptr}, 60 {0x0100000000000824, "PlatformConfigIcosaMariko", nullptr},
62 {0x0100000000000825, "ApplicationBlackList", nullptr}, 61 {0x0100000000000825, "ApplicationBlackList", nullptr},
63 {0x0100000000000826, "RebootlessSystemUpdateVersion", nullptr}, 62 {0x0100000000000826, "RebootlessSystemUpdateVersion", nullptr},
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index bb1b68778..0ea851a74 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -15,6 +15,7 @@ bool Object::IsWaitable() const {
15 switch (GetHandleType()) { 15 switch (GetHandleType()) {
16 case HandleType::ReadableEvent: 16 case HandleType::ReadableEvent:
17 case HandleType::Thread: 17 case HandleType::Thread:
18 case HandleType::Process:
18 case HandleType::Timer: 19 case HandleType::Timer:
19 case HandleType::ServerPort: 20 case HandleType::ServerPort:
20 case HandleType::ServerSession: 21 case HandleType::ServerSession:
@@ -23,7 +24,6 @@ bool Object::IsWaitable() const {
23 case HandleType::Unknown: 24 case HandleType::Unknown:
24 case HandleType::WritableEvent: 25 case HandleType::WritableEvent:
25 case HandleType::SharedMemory: 26 case HandleType::SharedMemory:
26 case HandleType::Process:
27 case HandleType::AddressArbiter: 27 case HandleType::AddressArbiter:
28 case HandleType::ResourceLimit: 28 case HandleType::ResourceLimit:
29 case HandleType::ClientPort: 29 case HandleType::ClientPort:
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 4ecb8c926..211bf6686 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -9,6 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/file_sys/program_metadata.h" 11#include "core/file_sys/program_metadata.h"
12#include "core/hle/kernel/errors.h"
12#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/resource_limit.h" 15#include "core/hle/kernel/resource_limit.h"
@@ -48,6 +49,21 @@ SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
48 return resource_limit; 49 return resource_limit;
49} 50}
50 51
52ResultCode Process::ClearSignalState() {
53 if (status == ProcessStatus::Exited) {
54 LOG_ERROR(Kernel, "called on a terminated process instance.");
55 return ERR_INVALID_STATE;
56 }
57
58 if (!is_signaled) {
59 LOG_ERROR(Kernel, "called on a process instance that isn't signaled.");
60 return ERR_INVALID_STATE;
61 }
62
63 is_signaled = false;
64 return RESULT_SUCCESS;
65}
66
51void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { 67void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
52 program_id = metadata.GetTitleID(); 68 program_id = metadata.GetTitleID();
53 is_64bit_process = metadata.Is64BitProgram(); 69 is_64bit_process = metadata.Is64BitProgram();
@@ -137,13 +153,13 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
137 .Unwrap(); 153 .Unwrap();
138 154
139 vm_manager.LogLayout(); 155 vm_manager.LogLayout();
140 status = ProcessStatus::Running; 156 ChangeStatus(ProcessStatus::Running);
141 157
142 Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this); 158 Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
143} 159}
144 160
145void Process::PrepareForTermination() { 161void Process::PrepareForTermination() {
146 status = ProcessStatus::Exited; 162 ChangeStatus(ProcessStatus::Exiting);
147 163
148 const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) { 164 const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
149 for (auto& thread : thread_list) { 165 for (auto& thread : thread_list) {
@@ -167,6 +183,8 @@ void Process::PrepareForTermination() {
167 stop_threads(system.Scheduler(1).GetThreadList()); 183 stop_threads(system.Scheduler(1).GetThreadList());
168 stop_threads(system.Scheduler(2).GetThreadList()); 184 stop_threads(system.Scheduler(2).GetThreadList());
169 stop_threads(system.Scheduler(3).GetThreadList()); 185 stop_threads(system.Scheduler(3).GetThreadList());
186
187 ChangeStatus(ProcessStatus::Exited);
170} 188}
171 189
172/** 190/**
@@ -265,7 +283,25 @@ ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
265 return vm_manager.UnmapRange(dst_addr, size); 283 return vm_manager.UnmapRange(dst_addr, size);
266} 284}
267 285
268Kernel::Process::Process(KernelCore& kernel) : Object{kernel} {} 286Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {}
269Kernel::Process::~Process() {} 287Kernel::Process::~Process() {}
270 288
289void Process::Acquire(Thread* thread) {
290 ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
291}
292
293bool Process::ShouldWait(Thread* thread) const {
294 return !is_signaled;
295}
296
297void Process::ChangeStatus(ProcessStatus new_status) {
298 if (status == new_status) {
299 return;
300 }
301
302 status = new_status;
303 is_signaled = true;
304 WakeupAllWaitingThreads();
305}
306
271} // namespace Kernel 307} // namespace Kernel
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 49345aa66..bcb9ac4b8 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -14,9 +14,10 @@
14#include "common/bit_field.h" 14#include "common/bit_field.h"
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "core/hle/kernel/handle_table.h" 16#include "core/hle/kernel/handle_table.h"
17#include "core/hle/kernel/object.h"
18#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
19#include "core/hle/kernel/vm_manager.h" 18#include "core/hle/kernel/vm_manager.h"
19#include "core/hle/kernel/wait_object.h"
20#include "core/hle/result.h"
20 21
21namespace FileSys { 22namespace FileSys {
22class ProgramMetadata; 23class ProgramMetadata;
@@ -117,7 +118,7 @@ struct CodeSet final {
117 VAddr entrypoint = 0; 118 VAddr entrypoint = 0;
118}; 119};
119 120
120class Process final : public Object { 121class Process final : public WaitObject {
121public: 122public:
122 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; 123 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
123 124
@@ -212,6 +213,16 @@ public:
212 return random_entropy.at(index); 213 return random_entropy.at(index);
213 } 214 }
214 215
216 /// Clears the signaled state of the process if and only if it's signaled.
217 ///
218 /// @pre The process must not be already terminated. If this is called on a
219 /// terminated process, then ERR_INVALID_STATE will be returned.
220 ///
221 /// @pre The process must be in a signaled state. If this is called on a
222 /// process instance that is not signaled, ERR_INVALID_STATE will be
223 /// returned.
224 ResultCode ClearSignalState();
225
215 /** 226 /**
216 * Loads process-specifics configuration info with metadata provided 227 * Loads process-specifics configuration info with metadata provided
217 * by an executable. 228 * by an executable.
@@ -260,6 +271,17 @@ private:
260 explicit Process(KernelCore& kernel); 271 explicit Process(KernelCore& kernel);
261 ~Process() override; 272 ~Process() override;
262 273
274 /// Checks if the specified thread should wait until this process is available.
275 bool ShouldWait(Thread* thread) const override;
276
277 /// Acquires/locks this process for the specified thread if it's available.
278 void Acquire(Thread* thread) override;
279
280 /// Changes the process status. If the status is different
281 /// from the current process status, then this will trigger
282 /// a process signal.
283 void ChangeStatus(ProcessStatus new_status);
284
263 /// Memory manager for this process. 285 /// Memory manager for this process.
264 Kernel::VMManager vm_manager; 286 Kernel::VMManager vm_manager;
265 287
@@ -305,6 +327,10 @@ private:
305 /// specified by metadata provided to the process during loading. 327 /// specified by metadata provided to the process during loading.
306 bool is_64bit_process = true; 328 bool is_64bit_process = true;
307 329
330 /// Whether or not this process is signaled. This occurs
331 /// upon the process changing to a different state.
332 bool is_signaled = false;
333
308 /// Total running time for the process in ticks. 334 /// Total running time for the process in ticks.
309 u64 total_process_running_time_ticks = 0; 335 u64 total_process_running_time_ticks = 0;
310 336
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp
index 92e16b4e6..ba01f495c 100644
--- a/src/core/hle/kernel/readable_event.cpp
+++ b/src/core/hle/kernel/readable_event.cpp
@@ -4,10 +4,10 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/hle/kernel/errors.h"
7#include "core/hle/kernel/object.h" 8#include "core/hle/kernel/object.h"
8#include "core/hle/kernel/readable_event.h" 9#include "core/hle/kernel/readable_event.h"
9#include "core/hle/kernel/thread.h" 10#include "core/hle/kernel/thread.h"
10#include "core/hle/kernel/writable_event.h"
11 11
12namespace Kernel { 12namespace Kernel {
13 13
@@ -34,6 +34,16 @@ void ReadableEvent::Clear() {
34 signaled = false; 34 signaled = false;
35} 35}
36 36
37ResultCode ReadableEvent::Reset() {
38 if (!signaled) {
39 return ERR_INVALID_STATE;
40 }
41
42 Clear();
43
44 return RESULT_SUCCESS;
45}
46
37void ReadableEvent::WakeupAllWaitingThreads() { 47void ReadableEvent::WakeupAllWaitingThreads() {
38 WaitObject::WakeupAllWaitingThreads(); 48 WaitObject::WakeupAllWaitingThreads();
39 49
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h
index 867ff3051..80b3b0aba 100644
--- a/src/core/hle/kernel/readable_event.h
+++ b/src/core/hle/kernel/readable_event.h
@@ -7,6 +7,8 @@
7#include "core/hle/kernel/object.h" 7#include "core/hle/kernel/object.h"
8#include "core/hle/kernel/wait_object.h" 8#include "core/hle/kernel/wait_object.h"
9 9
10union ResultCode;
11
10namespace Kernel { 12namespace Kernel {
11 13
12class KernelCore; 14class KernelCore;
@@ -39,8 +41,17 @@ public:
39 41
40 void WakeupAllWaitingThreads() override; 42 void WakeupAllWaitingThreads() override;
41 43
44 /// Unconditionally clears the readable event's state.
42 void Clear(); 45 void Clear();
43 46
47 /// Clears the readable event's state if and only if it
48 /// has already been signaled.
49 ///
50 /// @pre The event must be in a signaled state. If this event
51 /// is in an unsignaled state and this function is called,
52 /// then ERR_INVALID_STATE will be returned.
53 ResultCode Reset();
54
44private: 55private:
45 explicit ReadableEvent(KernelCore& kernel); 56 explicit ReadableEvent(KernelCore& kernel);
46 57
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index e6c77f9db..84df2040e 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1433,17 +1433,24 @@ static ResultCode CloseHandle(Handle handle) {
1433 return handle_table.Close(handle); 1433 return handle_table.Close(handle);
1434} 1434}
1435 1435
1436/// Reset an event 1436/// Clears the signaled state of an event or process.
1437static ResultCode ResetSignal(Handle handle) { 1437static ResultCode ResetSignal(Handle handle) {
1438 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); 1438 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
1439 1439
1440 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1440 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1441
1441 auto event = handle_table.Get<ReadableEvent>(handle); 1442 auto event = handle_table.Get<ReadableEvent>(handle);
1443 if (event) {
1444 return event->Reset();
1445 }
1442 1446
1443 ASSERT(event != nullptr); 1447 auto process = handle_table.Get<Process>(handle);
1448 if (process) {
1449 return process->ClearSignalState();
1450 }
1444 1451
1445 event->Clear(); 1452 LOG_ERROR(Kernel_SVC, "Invalid handle (0x{:08X})", handle);
1446 return RESULT_SUCCESS; 1453 return ERR_INVALID_HANDLE;
1447} 1454}
1448 1455
1449/// Creates a TransferMemory object 1456/// Creates a TransferMemory object
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index ca119dd3a..453d90a22 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -335,10 +335,7 @@ public:
335 vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, 335 vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
336 Kernel::VMAPermission::ReadWrite); 336 Kernel::VMAPermission::ReadWrite);
337 337
338 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); 338 Core::System::GetInstance().InvalidateCpuInstructionCaches();
339 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
340 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
341 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
342 339
343 nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size}); 340 nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size});
344 341
@@ -391,10 +388,7 @@ public:
391 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); 388 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
392 ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS); 389 ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
393 390
394 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); 391 Core::System::GetInstance().InvalidateCpuInstructionCaches();
395 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
396 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
397 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
398 392
399 nro.erase(iter); 393 nro.erase(iter);
400 IPC::ResponseBuilder rb{ctx, 2}; 394 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 70abd856a..41fd2a6a0 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -53,6 +53,14 @@ void PageTable::Resize(std::size_t address_space_width_in_bits) {
53 53
54 pointers.resize(num_page_table_entries); 54 pointers.resize(num_page_table_entries);
55 attributes.resize(num_page_table_entries); 55 attributes.resize(num_page_table_entries);
56
57 // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
58 // vector size is subsequently decreased (via resize), the vector might not automatically
59 // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
60 // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
61
62 pointers.shrink_to_fit();
63 attributes.shrink_to_fit();
56} 64}
57 65
58static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) { 66static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index b9faaf8e0..5ea094e64 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -1049,6 +1049,7 @@ union Instruction {
1049 BitField<49, 1, u64> nodep_flag; 1049 BitField<49, 1, u64> nodep_flag;
1050 BitField<50, 3, u64> component_mask_selector; 1050 BitField<50, 3, u64> component_mask_selector;
1051 BitField<53, 4, u64> texture_info; 1051 BitField<53, 4, u64> texture_info;
1052 BitField<60, 1, u64> fp32_flag;
1052 1053
1053 TextureType GetTextureType() const { 1054 TextureType GetTextureType() const {
1054 // The TEXS instruction has a weird encoding for the texture type. 1055 // The TEXS instruction has a weird encoding for the texture type.
@@ -1549,7 +1550,7 @@ private:
1549 INST("1110111011011---", Id::STG, Type::Memory, "STG"), 1550 INST("1110111011011---", Id::STG, Type::Memory, "STG"),
1550 INST("110000----111---", Id::TEX, Type::Memory, "TEX"), 1551 INST("110000----111---", Id::TEX, Type::Memory, "TEX"),
1551 INST("1101111101001---", Id::TXQ, Type::Memory, "TXQ"), 1552 INST("1101111101001---", Id::TXQ, Type::Memory, "TXQ"),
1552 INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), 1553 INST("1101-00---------", Id::TEXS, Type::Memory, "TEXS"),
1553 INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), 1554 INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"),
1554 INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"), 1555 INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"),
1555 INST("1101111100------", Id::TLD4S, Type::Memory, "TLD4S"), 1556 INST("1101111100------", Id::TLD4S, Type::Memory, "TLD4S"),
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 9e93bd609..2b29fc45f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -79,6 +79,26 @@ struct DrawParameters {
79 } 79 }
80}; 80};
81 81
82struct FramebufferCacheKey {
83 bool is_single_buffer = false;
84 bool stencil_enable = false;
85
86 std::array<GLenum, Maxwell::NumRenderTargets> color_attachments{};
87 std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> colors{};
88 u32 colors_count = 0;
89
90 GLuint zeta = 0;
91
92 auto Tie() const {
93 return std::tie(is_single_buffer, stencil_enable, color_attachments, colors, colors_count,
94 zeta);
95 }
96
97 bool operator<(const FramebufferCacheKey& rhs) const {
98 return Tie() < rhs.Tie();
99 }
100};
101
82RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) 102RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info)
83 : res_cache{*this}, shader_cache{*this}, emu_window{window}, screen_info{info}, 103 : res_cache{*this}, shader_cache{*this}, emu_window{window}, screen_info{info},
84 buffer_cache(*this, STREAM_BUFFER_SIZE) { 104 buffer_cache(*this, STREAM_BUFFER_SIZE) {
@@ -90,9 +110,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
90 110
91 OpenGLState::ApplyDefaultState(); 111 OpenGLState::ApplyDefaultState();
92 112
93 // Create render framebuffer
94 framebuffer.Create();
95
96 shader_program_manager = std::make_unique<GLShader::ProgramManager>(); 113 shader_program_manager = std::make_unique<GLShader::ProgramManager>();
97 state.draw.shader_program = 0; 114 state.draw.shader_program = 0;
98 state.Apply(); 115 state.Apply();
@@ -361,6 +378,44 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
361 SyncClipEnabled(clip_distances); 378 SyncClipEnabled(clip_distances);
362} 379}
363 380
381void RasterizerOpenGL::SetupCachedFramebuffer(const FramebufferCacheKey& fbkey,
382 OpenGLState& current_state) {
383 const auto [entry, is_cache_miss] = framebuffer_cache.try_emplace(fbkey);
384 auto& framebuffer = entry->second;
385
386 if (is_cache_miss)
387 framebuffer.Create();
388
389 current_state.draw.draw_framebuffer = framebuffer.handle;
390 current_state.ApplyFramebufferState();
391
392 if (!is_cache_miss)
393 return;
394
395 if (fbkey.is_single_buffer) {
396 if (fbkey.color_attachments[0] != GL_NONE) {
397 glFramebufferTexture(GL_DRAW_FRAMEBUFFER, fbkey.color_attachments[0], fbkey.colors[0],
398 0);
399 }
400 glDrawBuffer(fbkey.color_attachments[0]);
401 } else {
402 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
403 if (fbkey.colors[index]) {
404 glFramebufferTexture(GL_DRAW_FRAMEBUFFER,
405 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
406 fbkey.colors[index], 0);
407 }
408 }
409 glDrawBuffers(fbkey.colors_count, fbkey.color_attachments.data());
410 }
411
412 if (fbkey.zeta) {
413 GLenum zeta_attachment =
414 fbkey.stencil_enable ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
415 glFramebufferTexture(GL_DRAW_FRAMEBUFFER, zeta_attachment, fbkey.zeta, 0);
416 }
417}
418
364std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 419std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
365 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 420 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
366 421
@@ -444,10 +499,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us
444 UNIMPLEMENTED_IF(regs.rt_separate_frag_data != 0); 499 UNIMPLEMENTED_IF(regs.rt_separate_frag_data != 0);
445 500
446 // Bind the framebuffer surfaces 501 // Bind the framebuffer surfaces
447 current_state.draw.draw_framebuffer = framebuffer.handle;
448 current_state.ApplyFramebufferState();
449 current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; 502 current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
450 503
504 FramebufferCacheKey fbkey;
505
451 if (using_color_fb) { 506 if (using_color_fb) {
452 if (single_color_target) { 507 if (single_color_target) {
453 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer 508 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer
@@ -463,14 +518,12 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us
463 state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion; 518 state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion;
464 } 519 }
465 520
466 glFramebufferTexture2D( 521 fbkey.is_single_buffer = true;
467 GL_DRAW_FRAMEBUFFER, 522 fbkey.color_attachments[0] =
468 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D, 523 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target);
469 color_surface != nullptr ? color_surface->Texture().handle : 0, 0); 524 fbkey.colors[0] = color_surface != nullptr ? color_surface->Texture().handle : 0;
470 glDrawBuffer(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target));
471 } else { 525 } else {
472 // Multiple color attachments are enabled 526 // Multiple color attachments are enabled
473 std::array<GLenum, Maxwell::NumRenderTargets> buffers;
474 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { 527 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
475 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); 528 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents);
476 529
@@ -485,22 +538,17 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us
485 color_surface->GetSurfaceParams().srgb_conversion; 538 color_surface->GetSurfaceParams().srgb_conversion;
486 } 539 }
487 540
488 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); 541 fbkey.color_attachments[index] =
489 glFramebufferTexture2D( 542 GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
490 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), 543 fbkey.colors[index] =
491 GL_TEXTURE_2D, color_surface != nullptr ? color_surface->Texture().handle : 0, 544 color_surface != nullptr ? color_surface->Texture().handle : 0;
492 0);
493 } 545 }
494 glDrawBuffers(regs.rt_control.count, buffers.data()); 546 fbkey.is_single_buffer = false;
547 fbkey.colors_count = regs.rt_control.count;
495 } 548 }
496 } else { 549 } else {
497 // No color attachments are enabled - zero out all of them 550 // No color attachments are enabled - leave them as zero
498 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { 551 fbkey.is_single_buffer = true;
499 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
500 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), GL_TEXTURE_2D,
501 0, 0);
502 }
503 glDrawBuffer(GL_NONE);
504 } 552 }
505 553
506 if (depth_surface) { 554 if (depth_surface) {
@@ -508,22 +556,12 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us
508 // the shader doesn't actually write to it. 556 // the shader doesn't actually write to it.
509 depth_surface->MarkAsModified(true, res_cache); 557 depth_surface->MarkAsModified(true, res_cache);
510 558
511 if (regs.stencil_enable) { 559 fbkey.zeta = depth_surface->Texture().handle;
512 // Attach both depth and stencil 560 fbkey.stencil_enable = regs.stencil_enable;
513 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
514 depth_surface->Texture().handle, 0);
515 } else {
516 // Attach depth
517 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
518 depth_surface->Texture().handle, 0);
519 // Clear stencil attachment
520 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
521 }
522 } else {
523 // Clear both depth and stencil attachment
524 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
525 0);
526 } 561 }
562
563 SetupCachedFramebuffer(fbkey, current_state);
564
527 SyncViewport(current_state); 565 SyncViewport(current_state);
528} 566}
529 567
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 988fa3e27..8a891ffc7 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -40,6 +40,7 @@ namespace OpenGL {
40 40
41struct ScreenInfo; 41struct ScreenInfo;
42struct DrawParameters; 42struct DrawParameters;
43struct FramebufferCacheKey;
43 44
44class RasterizerOpenGL : public VideoCore::RasterizerInterface { 45class RasterizerOpenGL : public VideoCore::RasterizerInterface {
45public: 46public:
@@ -195,11 +196,12 @@ private:
195 OGLVertexArray> 196 OGLVertexArray>
196 vertex_array_cache; 197 vertex_array_cache;
197 198
199 std::map<FramebufferCacheKey, OGLFramebuffer> framebuffer_cache;
200
198 std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers; 201 std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers;
199 202
200 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; 203 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
201 OGLBufferCache buffer_cache; 204 OGLBufferCache buffer_cache;
202 OGLFramebuffer framebuffer;
203 PrimitiveAssembler primitive_assembler{buffer_cache}; 205 PrimitiveAssembler primitive_assembler{buffer_cache};
204 GLint uniform_buffer_alignment; 206 GLint uniform_buffer_alignment;
205 207
@@ -214,6 +216,8 @@ private:
214 216
215 void SetupShaders(GLenum primitive_mode); 217 void SetupShaders(GLenum primitive_mode);
216 218
219 void SetupCachedFramebuffer(const FramebufferCacheKey& fbkey, OpenGLState& current_state);
220
217 enum class AccelDraw { Disabled, Arrays, Indexed }; 221 enum class AccelDraw { Disabled, Arrays, Indexed };
218 AccelDraw accelerate_draw = AccelDraw::Disabled; 222 AccelDraw accelerate_draw = AccelDraw::Disabled;
219 223
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 8d68156bf..4fc09cac6 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -50,6 +50,14 @@ public:
50 using std::runtime_error::runtime_error; 50 using std::runtime_error::runtime_error;
51}; 51};
52 52
53/// Generates code to use for a swizzle operation.
54static std::string GetSwizzle(u64 elem) {
55 ASSERT(elem <= 3);
56 std::string swizzle = ".";
57 swizzle += "xyzw"[elem];
58 return swizzle;
59}
60
53/// Translate topology 61/// Translate topology
54static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { 62static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
55 switch (topology) { 63 switch (topology) {
@@ -1004,14 +1012,6 @@ private:
1004 } 1012 }
1005 } 1013 }
1006 1014
1007 /// Generates code to use for a swizzle operation.
1008 static std::string GetSwizzle(u64 elem) {
1009 ASSERT(elem <= 3);
1010 std::string swizzle = ".";
1011 swizzle += "xyzw"[elem];
1012 return swizzle;
1013 }
1014
1015 ShaderWriter& shader; 1015 ShaderWriter& shader;
1016 ShaderWriter& declarations; 1016 ShaderWriter& declarations;
1017 std::vector<GLSLRegister> regs; 1017 std::vector<GLSLRegister> regs;
@@ -1343,7 +1343,7 @@ private:
1343 regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); 1343 regs.SetRegisterToInteger(dest, true, 0, result, 1, 1);
1344 } 1344 }
1345 1345
1346 void WriteTexsInstruction(const Instruction& instr, const std::string& texture) { 1346 void WriteTexsInstructionFloat(const Instruction& instr, const std::string& texture) {
1347 // TEXS has two destination registers and a swizzle. The first two elements in the swizzle 1347 // TEXS has two destination registers and a swizzle. The first two elements in the swizzle
1348 // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 1348 // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
1349 1349
@@ -1368,6 +1368,38 @@ private:
1368 } 1368 }
1369 } 1369 }
1370 1370
1371 void WriteTexsInstructionHalfFloat(const Instruction& instr, const std::string& texture) {
1372 // TEXS.F16 destionation registers are packed in two registers in pairs (just like any half
1373 // float instruction).
1374
1375 std::array<std::string, 4> components;
1376 u32 written_components = 0;
1377
1378 for (u32 component = 0; component < 4; ++component) {
1379 if (!instr.texs.IsComponentEnabled(component))
1380 continue;
1381 components[written_components++] = texture + GetSwizzle(component);
1382 }
1383 if (written_components == 0)
1384 return;
1385
1386 const auto BuildComponent = [&](std::string low, std::string high, bool high_enabled) {
1387 return "vec2(" + low + ", " + (high_enabled ? high : "0") + ')';
1388 };
1389
1390 regs.SetRegisterToHalfFloat(
1391 instr.gpr0, 0, BuildComponent(components[0], components[1], written_components > 1),
1392 Tegra::Shader::HalfMerge::H0_H1, 1, 1);
1393
1394 if (written_components > 2) {
1395 ASSERT(instr.texs.HasTwoDestinations());
1396 regs.SetRegisterToHalfFloat(
1397 instr.gpr28, 0,
1398 BuildComponent(components[2], components[3], written_components > 3),
1399 Tegra::Shader::HalfMerge::H0_H1, 1, 1);
1400 }
1401 }
1402
1371 static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { 1403 static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) {
1372 switch (texture_type) { 1404 switch (texture_type) {
1373 case Tegra::Shader::TextureType::Texture1D: 1405 case Tegra::Shader::TextureType::Texture1D:
@@ -2766,24 +2798,27 @@ private:
2766 const bool depth_compare = 2798 const bool depth_compare =
2767 instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2799 instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2768 const auto process_mode = instr.texs.GetTextureProcessMode(); 2800 const auto process_mode = instr.texs.GetTextureProcessMode();
2801
2769 UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2802 UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2770 "NODEP is not implemented"); 2803 "NODEP is not implemented");
2771 2804
2772 const auto scope = shader.Scope(); 2805 const auto scope = shader.Scope();
2773 2806
2774 const auto [coord, texture] = 2807 auto [coord, texture] =
2775 GetTEXSCode(instr, texture_type, process_mode, depth_compare, is_array); 2808 GetTEXSCode(instr, texture_type, process_mode, depth_compare, is_array);
2776 2809
2777 shader.AddLine(coord); 2810 shader.AddLine(coord);
2778 2811
2779 if (!depth_compare) { 2812 if (depth_compare) {
2780 shader.AddLine("vec4 texture_tmp = " + texture + ';'); 2813 texture = "vec4(" + texture + ')';
2814 }
2815 shader.AddLine("vec4 texture_tmp = " + texture + ';');
2781 2816
2817 if (instr.texs.fp32_flag) {
2818 WriteTexsInstructionFloat(instr, "texture_tmp");
2782 } else { 2819 } else {
2783 shader.AddLine("vec4 texture_tmp = vec4(" + texture + ");"); 2820 WriteTexsInstructionHalfFloat(instr, "texture_tmp");
2784 } 2821 }
2785
2786 WriteTexsInstruction(instr, "texture_tmp");
2787 break; 2822 break;
2788 } 2823 }
2789 case OpCode::Id::TLDS: { 2824 case OpCode::Id::TLDS: {
@@ -2842,7 +2877,7 @@ private:
2842 } 2877 }
2843 }(); 2878 }();
2844 2879
2845 WriteTexsInstruction(instr, texture); 2880 WriteTexsInstructionFloat(instr, texture);
2846 break; 2881 break;
2847 } 2882 }
2848 case OpCode::Id::TLD4: { 2883 case OpCode::Id::TLD4: {
@@ -2940,7 +2975,8 @@ private:
2940 if (depth_compare) { 2975 if (depth_compare) {
2941 texture = "vec4(" + texture + ')'; 2976 texture = "vec4(" + texture + ')';
2942 } 2977 }
2943 WriteTexsInstruction(instr, texture); 2978
2979 WriteTexsInstructionFloat(instr, texture);
2944 break; 2980 break;
2945 } 2981 }
2946 case OpCode::Id::TXQ: { 2982 case OpCode::Id::TXQ: {