diff options
| -rw-r--r-- | src/core/arm/arm_interface.h | 4 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic.cpp | 8 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic.h | 2 | ||||
| -rw-r--r-- | src/core/arm/unicorn/arm_unicorn.cpp | 10 | ||||
| -rw-r--r-- | src/core/arm/unicorn/arm_unicorn.h | 2 | ||||
| -rw-r--r-- | src/core/file_sys/errors.h | 16 | ||||
| -rw-r--r-- | src/core/file_sys/vfs.cpp | 2 | ||||
| -rw-r--r-- | src/core/file_sys/vfs.h | 27 | ||||
| -rw-r--r-- | src/core/file_sys/vfs_offset.cpp | 5 | ||||
| -rw-r--r-- | src/core/file_sys/vfs_offset.h | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.cpp | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 9 | ||||
| -rw-r--r-- | src/tests/core/arm/arm_test_common.cpp | 9 | ||||
| -rw-r--r-- | src/tests/core/arm/arm_test_common.h | 7 | ||||
| -rw-r--r-- | src/video_core/gpu.cpp | 6 | ||||
| -rw-r--r-- | src/video_core/gpu.h | 9 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 10 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_manager.cpp | 5 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_manager.h | 1 | ||||
| -rw-r--r-- | src/yuzu/debugger/graphics/graphics_surface.cpp | 6 | 
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; | 
