summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/arm/arm_interface.h4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp8
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h2
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp10
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h2
-rw-r--r--src/core/file_sys/errors.h16
-rw-r--r--src/core/file_sys/vfs.cpp2
-rw-r--r--src/core/file_sys/vfs.h27
-rw-r--r--src/core/file_sys/vfs_offset.cpp5
-rw-r--r--src/core/file_sys/vfs_offset.h2
-rw-r--r--src/core/hle/kernel/scheduler.cpp3
-rw-r--r--src/core/hle/kernel/thread.cpp1
-rw-r--r--src/core/hle/kernel/thread.h9
-rw-r--r--src/tests/core/arm/arm_test_common.cpp9
-rw-r--r--src/tests/core/arm/arm_test_common.h7
-rw-r--r--src/video_core/gpu.cpp6
-rw-r--r--src/video_core/gpu.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h1
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp6
21 files changed, 94 insertions, 50 deletions
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 8416e73b0..28a99defe 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -104,6 +104,10 @@ public:
virtual void SetTlsAddress(VAddr address) = 0;
+ virtual u64 GetTPIDR_EL0() const = 0;
+
+ virtual void SetTPIDR_EL0(u64 value) = 0;
+
/**
* Saves the current CPU context
* @param ctx Thread context to save
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 3572ee7b9..df47d5ee8 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -196,6 +196,14 @@ void ARM_Dynarmic::SetTlsAddress(u64 address) {
cb->tpidrro_el0 = address;
}
+u64 ARM_Dynarmic::GetTPIDR_EL0() const {
+ return cb->tpidr_el0;
+}
+
+void ARM_Dynarmic::SetTPIDR_EL0(u64 value) {
+ cb->tpidr_el0 = value;
+}
+
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index ed724c3f1..a9891ac4f 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -34,6 +34,8 @@ public:
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
+ void SetTPIDR_EL0(u64 value) override;
+ u64 GetTPIDR_EL0() const override;
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index d2d699e9b..44a46bf04 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -169,6 +169,16 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
}
+u64 ARM_Unicorn::GetTPIDR_EL0() const {
+ u64 value{};
+ CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDR_EL0, &value));
+ return value;
+}
+
+void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
+ CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value));
+}
+
void ARM_Unicorn::Run() {
if (GDBStub::IsServerEnabled()) {
ExecuteInstructions(std::max(4000000, 0));
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index a78a0acf2..af7943352 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -28,6 +28,8 @@ public:
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
+ void SetTPIDR_EL0(u64 value) override;
+ u64 GetTPIDR_EL0() const override;
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index a152dbd33..fea0593c7 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -20,13 +20,13 @@ enum {
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
// TODO(bunnei): Replace these with correct errors for Switch OS
-constexpr ResultCode ERROR_INVALID_PATH(ResultCode(-1));
-constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ResultCode(-1));
-constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ResultCode(-1));
-constexpr ResultCode ERROR_FILE_NOT_FOUND(ResultCode(-1));
-constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ResultCode(-1));
-constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ResultCode(-1));
-constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ResultCode(-1));
-constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(ResultCode(-1));
+constexpr ResultCode ERROR_INVALID_PATH(-1);
+constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
+constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
+constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
+constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
+constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
+constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
+constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
} // namespace FileSys
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 16c8ad90b..3f690f12a 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -42,7 +42,7 @@ bool VfsFile::WriteByte(u8 data, size_t offset) {
return Write(&data, 1, offset) == 1;
}
-size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) {
+size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
return Write(data.data(), data.size(), offset);
}
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index a5213e0cc..db3c77eac 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -59,8 +59,7 @@ struct VfsFile : NonCopyable {
// Returns the number of bytes (sizeof(T)*number_elements) read successfully.
template <typename T>
size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
+ static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
}
@@ -69,8 +68,7 @@ struct VfsFile : NonCopyable {
// Returns the number of bytes read successfully.
template <typename T>
size_t ReadBytes(T* data, size_t size, size_t offset = 0) const {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
+ static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), size, offset);
}
@@ -78,8 +76,7 @@ struct VfsFile : NonCopyable {
// Returns the number of bytes read successfully (sizeof(T)).
template <typename T>
size_t ReadObject(T* data, size_t offset = 0) const {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
+ static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
}
@@ -88,33 +85,29 @@ struct VfsFile : NonCopyable {
virtual bool WriteByte(u8 data, size_t offset = 0);
// Writes a vector of bytes to offset in file and returns the number of bytes successfully
// written.
- virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0);
+ virtual size_t WriteBytes(const std::vector<u8>& data, size_t offset = 0);
// Writes an array of type T, size number_elements to offset in file.
// Returns the number of bytes (sizeof(T)*number_elements) written successfully.
template <typename T>
- size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
-
+ size_t WriteArray(const T* data, size_t number_elements, size_t offset = 0) {
+ static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(data, number_elements * sizeof(T), offset);
}
// Writes size bytes starting at memory location data to offset in file.
// Returns the number of bytes written successfully.
template <typename T>
- size_t WriteBytes(T* data, size_t size, size_t offset = 0) {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
- return Write(reinterpret_cast<u8*>(data), size, offset);
+ size_t WriteBytes(const T* data, size_t size, size_t offset = 0) {
+ static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
+ return Write(reinterpret_cast<const u8*>(data), size, offset);
}
// Writes one object of type T to offset in file.
// Returns the number of bytes written successfully (sizeof(T)).
template <typename T>
size_t WriteObject(const T& data, size_t offset = 0) {
- static_assert(std::is_trivially_copyable<T>::value,
- "Data type must be trivially copyable.");
+ static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(&data, sizeof(T), offset);
}
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index 31fdd9aa1..217e02235 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <utility>
#include "core/file_sys/vfs_offset.h"
@@ -75,7 +76,7 @@ bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) {
return false;
}
-size_t OffsetVfsFile::WriteBytes(std::vector<u8> data, size_t r_offset) {
+size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) {
return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
}
@@ -88,7 +89,7 @@ size_t OffsetVfsFile::GetOffset() const {
}
size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const {
- return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0);
+ return std::clamp(r_size, size_t{0}, size - r_offset);
}
} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index 2e16e47eb..ded4827f5 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -28,7 +28,7 @@ struct OffsetVfsFile : public VfsFile {
std::vector<u8> ReadBytes(size_t size, size_t offset) const override;
std::vector<u8> ReadAllBytes() const override;
bool WriteByte(u8 data, size_t offset) override;
- size_t WriteBytes(std::vector<u8> data, size_t offset) override;
+ size_t WriteBytes(const std::vector<u8>& data, size_t offset) override;
bool Rename(const std::string& name) override;
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index f7e25cbf5..e307eec98 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -56,6 +56,8 @@ void Scheduler::SwitchContext(Thread* new_thread) {
if (previous_thread) {
previous_thread->last_running_ticks = CoreTiming::GetTicks();
cpu_core->SaveContext(previous_thread->context);
+ // Save the TPIDR_EL0 system register in case it was modified.
+ previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0();
if (previous_thread->status == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
@@ -87,6 +89,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
cpu_core->LoadContext(new_thread->context);
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
+ cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
cpu_core->ClearExclusiveState();
} else {
current_thread = nullptr;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 53f2e861e..cd85c4b7c 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -312,6 +312,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->status = ThreadStatus::Dormant;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
+ thread->tpidr_el0 = 0;
thread->nominal_priority = thread->current_priority = priority;
thread->last_running_ticks = CoreTiming::GetTicks();
thread->processor_id = processor_id;
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 47881ec20..6218960d2 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -183,6 +183,14 @@ public:
}
/*
+ * Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
+ * @returns The value of the TPIDR_EL0 register.
+ */
+ u64 GetTPIDR_EL0() const {
+ return tpidr_el0;
+ }
+
+ /*
* Returns the address of the current thread's command buffer, located in the TLS.
* @returns VAddr of the thread's command buffer.
*/
@@ -213,6 +221,7 @@ public:
s32 processor_id;
VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
+ u64 tpidr_el0; ///< TPIDR_EL0 read/write system register.
SharedPtr<Process> owner_process; ///< Process that owns this thread
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 7f9f27e19..539746246 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -10,8 +10,6 @@
namespace ArmTests {
-static Memory::PageTable* page_table = nullptr;
-
TestEnvironment::TestEnvironment(bool mutable_memory_)
: mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
@@ -67,10 +65,13 @@ boost::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
}
boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
- auto iter = data.find(addr);
+ const auto iter = data.find(addr);
+
if (iter == data.end()) {
- return addr; // Some arbitrary data
+ // Some arbitrary data
+ return static_cast<u8>(addr);
}
+
return iter->second;
}
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h
index b66922d61..7fdbda494 100644
--- a/src/tests/core/arm/arm_test_common.h
+++ b/src/tests/core/arm/arm_test_common.h
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#pragma once
+
#include <tuple>
#include <unordered_map>
#include <vector>
@@ -9,6 +11,10 @@
#include "common/common_types.h"
#include "core/memory_hook.h"
+namespace Memory {
+struct PageTable;
+}
+
namespace ArmTests {
struct WriteRecord {
@@ -79,6 +85,7 @@ private:
bool mutable_memory;
std::shared_ptr<TestMemory> test_memory;
std::vector<WriteRecord> write_records;
+ Memory::PageTable* page_table = nullptr;
};
} // namespace ArmTests
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index e36483145..a003bc9e3 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -20,7 +20,11 @@ GPU::GPU() {
GPU::~GPU() = default;
-const Tegra::Engines::Maxwell3D& GPU::Get3DEngine() const {
+const Engines::Maxwell3D& GPU::Maxwell3D() const {
+ return *maxwell_3d;
+}
+
+Engines::Maxwell3D& GPU::Maxwell3D() {
return *maxwell_3d;
}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 60930e997..a32148ecd 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -93,15 +93,14 @@ public:
/// Processes a command list stored at the specified address in GPU memory.
void ProcessCommandList(GPUVAddr address, u32 size);
+ /// Returns a const reference to the Maxwell3D GPU engine.
+ const Engines::Maxwell3D& Maxwell3D() const;
+
/// Returns a reference to the Maxwell3D GPU engine.
- const Engines::Maxwell3D& Get3DEngine() const;
+ Engines::Maxwell3D& Maxwell3D();
std::unique_ptr<MemoryManager> memory_manager;
- Engines::Maxwell3D& Maxwell3D() {
- return *maxwell_3d;
- }
-
private:
/// Writes a single register in the engine bound to the specified subchannel
void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index f75999557..65a2fd5e8 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -634,8 +634,8 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program,
u32 current_bindpoint,
const std::vector<GLShader::ConstBufferEntry>& entries) {
- auto& gpu = Core::System::GetInstance().GPU();
- auto& maxwell3d = gpu.Get3DEngine();
+ const auto& gpu = Core::System::GetInstance().GPU();
+ const auto& maxwell3d = gpu.Maxwell3D();
// Reset all buffer draw state for this stage.
for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) {
@@ -644,7 +644,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
}
// Upload only the enabled buffers from the 16 constbuffers of each shader stage
- auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)];
+ const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)];
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& used_buffer = entries[bindpoint];
@@ -700,8 +700,8 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit,
const std::vector<GLShader::SamplerEntry>& entries) {
- auto& gpu = Core::System::GetInstance().GPU();
- auto& maxwell3d = gpu.Get3DEngine();
+ const auto& gpu = Core::System::GetInstance().GPU();
+ const auto& maxwell3d = gpu.Maxwell3D();
ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units),
"Exceeded the number of active textures.");
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 1aa437f76..e81fcbbc4 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -10,8 +10,9 @@
namespace GLShader {
namespace Impl {
-void SetShaderUniformBlockBinding(GLuint shader, const char* name,
- Maxwell3D::Regs::ShaderStage binding, size_t expected_size) {
+static void SetShaderUniformBlockBinding(GLuint shader, const char* name,
+ Maxwell3D::Regs::ShaderStage binding,
+ size_t expected_size) {
GLuint ub_index = glGetUniformBlockIndex(shader, name);
if (ub_index != GL_INVALID_INDEX) {
GLint ub_size = 0;
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 4295c20a6..e29d551e1 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -21,7 +21,6 @@ using Tegra::Engines::Maxwell3D;
namespace Impl {
void SetShaderUniformBlockBindings(GLuint shader);
-void SetShaderSamplerBindings(GLuint shader);
} // namespace Impl
/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 1fbca8ad0..c41ff693b 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -336,9 +336,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
// TODO: Store a reference to the registers in the debug context instead of accessing them
// directly...
- auto& registers = gpu.Get3DEngine().regs;
- auto& rt = registers.rt[static_cast<size_t>(surface_source) -
- static_cast<size_t>(Source::RenderTarget0)];
+ const auto& registers = gpu.Maxwell3D().regs;
+ const auto& rt = registers.rt[static_cast<size_t>(surface_source) -
+ static_cast<size_t>(Source::RenderTarget0)];
surface_address = rt.Address();
surface_width = rt.width;