diff options
27 files changed, 349 insertions, 146 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 5753b871a..12f6d0114 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -13,7 +13,7 @@ #include <vector> #ifdef _WIN32 #include <share.h> // For _SH_DENYWR -#include <windows.h> // For OutputDebugStringA +#include <windows.h> // For OutputDebugStringW #else #define _SH_DENYWR 0 #endif @@ -148,7 +148,7 @@ void FileBackend::Write(const Entry& entry) { void DebuggerBackend::Write(const Entry& entry) { #ifdef _WIN32 - ::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str()); + ::OutputDebugStringW(Common::UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); #endif } 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{ VirtualDir NgWord1() { std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES); - for (std::size_t i = 0; i < NgWord1Data::NUMBER_WORD_TXT_FILES; ++i) { + for (std::size_t i = 0; i < files.size(); ++i) { files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)); } @@ -39,4 +39,43 @@ VirtualDir NgWord1() { return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); } +namespace NgWord2Data { + +constexpr std::size_t NUMBER_AC_NX_FILES = 0x10; + +// Should this archive replacement mysteriously not work on a future game, consider updating. +constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x15}; // 5.1.0 System Version + +constexpr std::array<u8, 0x2C> AC_NX_DATA{ + 0x1F, 0x8B, 0x08, 0x08, 0xD5, 0x2C, 0x09, 0x5C, 0x04, 0x00, 0x61, 0x63, 0x72, 0x61, 0x77, + 0x00, 0xED, 0xC1, 0x01, 0x0D, 0x00, 0x00, 0x00, 0xC2, 0x20, 0xFB, 0xA7, 0xB6, 0xC7, 0x07, + 0x0C, 0x00, 0x00, 0x00, 0xC8, 0x3B, 0x11, 0x00, 0x1C, 0xC7, 0x00, 0x10, 0x00, 0x00, +}; // Deserializes to no bad words + +} // namespace NgWord2Data + +VirtualDir NgWord2() { + std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3); + + for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) { + files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)); + files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i)); + files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i)); + } + + files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, "ac_common_b1_nx")); + files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, "ac_common_b2_nx")); + files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( + NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx")); + files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>( + NgWord2Data::VERSION_DAT, "version.dat")); + + return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); +} + } // 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 @@ namespace FileSys::SystemArchive { VirtualDir NgWord1(); +VirtualDir NgWord2(); } // 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <functional> #include "common/logging/log.h" #include "core/file_sys/romfs.h" #include "core/file_sys/system_archive/ng_word.h" @@ -13,7 +12,7 @@ namespace FileSys::SystemArchive { constexpr u64 SYSTEM_ARCHIVE_BASE_TITLE_ID = 0x0100000000000800; constexpr std::size_t SYSTEM_ARCHIVE_COUNT = 0x28; -using SystemArchiveSupplier = std::function<VirtualDir()>; +using SystemArchiveSupplier = VirtualDir (*)(); struct SystemArchiveDescriptor { u64 title_id; @@ -21,7 +20,7 @@ struct SystemArchiveDescriptor { SystemArchiveSupplier supplier; }; -const std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES = {{ +constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES{{ {0x0100000000000800, "CertStore", nullptr}, {0x0100000000000801, "ErrorMessage", nullptr}, {0x0100000000000802, "MiiModel", nullptr}, @@ -57,7 +56,7 @@ const std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES {0x0100000000000820, "PlatformConfigCopper", nullptr}, {0x0100000000000821, "PlatformConfigHoag", nullptr}, {0x0100000000000822, "ControllerFirmware", nullptr}, - {0x0100000000000823, "NgWord2", nullptr}, + {0x0100000000000823, "NgWord2", &NgWord2}, {0x0100000000000824, "PlatformConfigIcosaMariko", nullptr}, {0x0100000000000825, "ApplicationBlackList", nullptr}, {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 { switch (GetHandleType()) { case HandleType::ReadableEvent: case HandleType::Thread: + case HandleType::Process: case HandleType::Timer: case HandleType::ServerPort: case HandleType::ServerSession: @@ -23,7 +24,6 @@ bool Object::IsWaitable() const { case HandleType::Unknown: case HandleType::WritableEvent: case HandleType::SharedMemory: - case HandleType::Process: case HandleType::AddressArbiter: case HandleType::ResourceLimit: 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 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/file_sys/program_metadata.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" @@ -48,6 +49,21 @@ SharedPtr<ResourceLimit> Process::GetResourceLimit() const { return resource_limit; } +ResultCode Process::ClearSignalState() { + if (status == ProcessStatus::Exited) { + LOG_ERROR(Kernel, "called on a terminated process instance."); + return ERR_INVALID_STATE; + } + + if (!is_signaled) { + LOG_ERROR(Kernel, "called on a process instance that isn't signaled."); + return ERR_INVALID_STATE; + } + + is_signaled = false; + return RESULT_SUCCESS; +} + void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { program_id = metadata.GetTitleID(); is_64bit_process = metadata.Is64BitProgram(); @@ -137,13 +153,13 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { .Unwrap(); vm_manager.LogLayout(); - status = ProcessStatus::Running; + ChangeStatus(ProcessStatus::Running); Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this); } void Process::PrepareForTermination() { - status = ProcessStatus::Exited; + ChangeStatus(ProcessStatus::Exiting); const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) { for (auto& thread : thread_list) { @@ -167,6 +183,8 @@ void Process::PrepareForTermination() { stop_threads(system.Scheduler(1).GetThreadList()); stop_threads(system.Scheduler(2).GetThreadList()); stop_threads(system.Scheduler(3).GetThreadList()); + + ChangeStatus(ProcessStatus::Exited); } /** @@ -265,7 +283,25 @@ ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { return vm_manager.UnmapRange(dst_addr, size); } -Kernel::Process::Process(KernelCore& kernel) : Object{kernel} {} +Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {} Kernel::Process::~Process() {} +void Process::Acquire(Thread* thread) { + ASSERT_MSG(!ShouldWait(thread), "Object unavailable!"); +} + +bool Process::ShouldWait(Thread* thread) const { + return !is_signaled; +} + +void Process::ChangeStatus(ProcessStatus new_status) { + if (status == new_status) { + return; + } + + status = new_status; + is_signaled = true; + WakeupAllWaitingThreads(); +} + } // 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 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" +#include "core/hle/kernel/wait_object.h" +#include "core/hle/result.h" namespace FileSys { class ProgramMetadata; @@ -117,7 +118,7 @@ struct CodeSet final { VAddr entrypoint = 0; }; -class Process final : public Object { +class Process final : public WaitObject { public: static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; @@ -212,6 +213,16 @@ public: return random_entropy.at(index); } + /// Clears the signaled state of the process if and only if it's signaled. + /// + /// @pre The process must not be already terminated. If this is called on a + /// terminated process, then ERR_INVALID_STATE will be returned. + /// + /// @pre The process must be in a signaled state. If this is called on a + /// process instance that is not signaled, ERR_INVALID_STATE will be + /// returned. + ResultCode ClearSignalState(); + /** * Loads process-specifics configuration info with metadata provided * by an executable. @@ -260,6 +271,17 @@ private: explicit Process(KernelCore& kernel); ~Process() override; + /// Checks if the specified thread should wait until this process is available. + bool ShouldWait(Thread* thread) const override; + + /// Acquires/locks this process for the specified thread if it's available. + void Acquire(Thread* thread) override; + + /// Changes the process status. If the status is different + /// from the current process status, then this will trigger + /// a process signal. + void ChangeStatus(ProcessStatus new_status); + /// Memory manager for this process. Kernel::VMManager vm_manager; @@ -305,6 +327,10 @@ private: /// specified by metadata provided to the process during loading. bool is_64bit_process = true; + /// Whether or not this process is signaled. This occurs + /// upon the process changing to a different state. + bool is_signaled = false; + /// Total running time for the process in ticks. u64 total_process_running_time_ticks = 0; 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 @@ #include <algorithm> #include "common/assert.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/thread.h" -#include "core/hle/kernel/writable_event.h" namespace Kernel { @@ -34,6 +34,16 @@ void ReadableEvent::Clear() { signaled = false; } +ResultCode ReadableEvent::Reset() { + if (!signaled) { + return ERR_INVALID_STATE; + } + + Clear(); + + return RESULT_SUCCESS; +} + void ReadableEvent::WakeupAllWaitingThreads() { WaitObject::WakeupAllWaitingThreads(); 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 @@ #include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" +union ResultCode; + namespace Kernel { class KernelCore; @@ -39,8 +41,17 @@ public: void WakeupAllWaitingThreads() override; + /// Unconditionally clears the readable event's state. void Clear(); + /// Clears the readable event's state if and only if it + /// has already been signaled. + /// + /// @pre The event must be in a signaled state. If this event + /// is in an unsignaled state and this function is called, + /// then ERR_INVALID_STATE will be returned. + ResultCode Reset(); + private: explicit ReadableEvent(KernelCore& kernel); 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) { return handle_table.Close(handle); } -/// Reset an event +/// Clears the signaled state of an event or process. static ResultCode ResetSignal(Handle handle) { LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + auto event = handle_table.Get<ReadableEvent>(handle); + if (event) { + return event->Reset(); + } - ASSERT(event != nullptr); + auto process = handle_table.Get<Process>(handle); + if (process) { + return process->ClearSignalState(); + } - event->Clear(); - return RESULT_SUCCESS; + LOG_ERROR(Kernel_SVC, "Invalid handle (0x{:08X})", handle); + return ERR_INVALID_HANDLE; } /// 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: vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, Kernel::VMAPermission::ReadWrite); - Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size}); @@ -391,10 +388,7 @@ public: Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS); - Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); nro.erase(iter); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index d109ed2b5..1615cb5a8 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -33,7 +33,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 6af76441c..a2d33021c 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -22,7 +22,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 7686634bf..5390ab9ee 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -131,7 +131,7 @@ public: * Returns the type of this file * @return FileType corresponding to the loaded file */ - virtual FileType GetFileType() = 0; + virtual FileType GetFileType() const = 0; /** * Load the application and return the created Process instance diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp index 42f4a777b..a093e3d36 100644 --- a/src/core/loader/nax.cpp +++ b/src/core/loader/nax.cpp @@ -37,7 +37,7 @@ FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) { return IdentifyTypeImpl(nax); } -FileType AppLoader_NAX::GetFileType() { +FileType AppLoader_NAX::GetFileType() const { return IdentifyTypeImpl(*nax); } diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h index b4d93bd01..0a97511b8 100644 --- a/src/core/loader/nax.h +++ b/src/core/loader/nax.h @@ -31,7 +31,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override; + FileType GetFileType() const override; ResultStatus Load(Kernel::Process& process) override; diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 95d9b73a1..cbbe701d2 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h @@ -29,7 +29,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 6deff3a51..013d629c0 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -33,7 +33,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 0c1defbb6..135b6ea5a 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -37,7 +37,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index db91cd01e..9ed8a59cf 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h @@ -31,7 +31,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 46f8dfc9e..ded5bb88a 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h @@ -31,7 +31,7 @@ public: */ static FileType IdentifyType(const FileSys::VirtualFile& file); - FileType GetFileType() override { + FileType GetFileType() const override { return IdentifyType(file); } 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 { BitField<49, 1, u64> nodep_flag; BitField<50, 3, u64> component_mask_selector; BitField<53, 4, u64> texture_info; + BitField<60, 1, u64> fp32_flag; TextureType GetTextureType() const { // The TEXS instruction has a weird encoding for the texture type. @@ -1549,7 +1550,7 @@ private: INST("1110111011011---", Id::STG, Type::Memory, "STG"), INST("110000----111---", Id::TEX, Type::Memory, "TEX"), INST("1101111101001---", Id::TXQ, Type::Memory, "TXQ"), - INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), + INST("1101-00---------", Id::TEXS, Type::Memory, "TEXS"), INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), INST("110010----111---", Id::TLD4, Type::Memory, "TLD4"), 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 { } }; +struct FramebufferCacheKey { + bool is_single_buffer = false; + bool stencil_enable = false; + + std::array<GLenum, Maxwell::NumRenderTargets> color_attachments{}; + std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> colors{}; + u32 colors_count = 0; + + GLuint zeta = 0; + + auto Tie() const { + return std::tie(is_single_buffer, stencil_enable, color_attachments, colors, colors_count, + zeta); + } + + bool operator<(const FramebufferCacheKey& rhs) const { + return Tie() < rhs.Tie(); + } +}; + RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) : res_cache{*this}, shader_cache{*this}, emu_window{window}, screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) { @@ -90,9 +110,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo OpenGLState::ApplyDefaultState(); - // Create render framebuffer - framebuffer.Create(); - shader_program_manager = std::make_unique<GLShader::ProgramManager>(); state.draw.shader_program = 0; state.Apply(); @@ -361,6 +378,44 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { SyncClipEnabled(clip_distances); } +void RasterizerOpenGL::SetupCachedFramebuffer(const FramebufferCacheKey& fbkey, + OpenGLState& current_state) { + const auto [entry, is_cache_miss] = framebuffer_cache.try_emplace(fbkey); + auto& framebuffer = entry->second; + + if (is_cache_miss) + framebuffer.Create(); + + current_state.draw.draw_framebuffer = framebuffer.handle; + current_state.ApplyFramebufferState(); + + if (!is_cache_miss) + return; + + if (fbkey.is_single_buffer) { + if (fbkey.color_attachments[0] != GL_NONE) { + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, fbkey.color_attachments[0], fbkey.colors[0], + 0); + } + glDrawBuffer(fbkey.color_attachments[0]); + } else { + for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { + if (fbkey.colors[index]) { + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), + fbkey.colors[index], 0); + } + } + glDrawBuffers(fbkey.colors_count, fbkey.color_attachments.data()); + } + + if (fbkey.zeta) { + GLenum zeta_attachment = + fbkey.stencil_enable ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, zeta_attachment, fbkey.zeta, 0); + } +} + std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; @@ -444,10 +499,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us UNIMPLEMENTED_IF(regs.rt_separate_frag_data != 0); // Bind the framebuffer surfaces - current_state.draw.draw_framebuffer = framebuffer.handle; - current_state.ApplyFramebufferState(); current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; + FramebufferCacheKey fbkey; + if (using_color_fb) { if (single_color_target) { // 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 state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion; } - glFramebufferTexture2D( - GL_DRAW_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D, - color_surface != nullptr ? color_surface->Texture().handle : 0, 0); - glDrawBuffer(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target)); + fbkey.is_single_buffer = true; + fbkey.color_attachments[0] = + GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target); + fbkey.colors[0] = color_surface != nullptr ? color_surface->Texture().handle : 0; } else { // Multiple color attachments are enabled - std::array<GLenum, Maxwell::NumRenderTargets> buffers; for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); @@ -485,22 +538,17 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us color_surface->GetSurfaceParams().srgb_conversion; } - buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); - glFramebufferTexture2D( - GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), - GL_TEXTURE_2D, color_surface != nullptr ? color_surface->Texture().handle : 0, - 0); + fbkey.color_attachments[index] = + GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); + fbkey.colors[index] = + color_surface != nullptr ? color_surface->Texture().handle : 0; } - glDrawBuffers(regs.rt_control.count, buffers.data()); + fbkey.is_single_buffer = false; + fbkey.colors_count = regs.rt_control.count; } } else { - // No color attachments are enabled - zero out all of them - for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), GL_TEXTURE_2D, - 0, 0); - } - glDrawBuffer(GL_NONE); + // No color attachments are enabled - leave them as zero + fbkey.is_single_buffer = true; } if (depth_surface) { @@ -508,22 +556,12 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us // the shader doesn't actually write to it. depth_surface->MarkAsModified(true, res_cache); - if (regs.stencil_enable) { - // Attach both depth and stencil - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - depth_surface->Texture().handle, 0); - } else { - // Attach depth - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - depth_surface->Texture().handle, 0); - // Clear stencil attachment - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - } - } else { - // Clear both depth and stencil attachment - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); + fbkey.zeta = depth_surface->Texture().handle; + fbkey.stencil_enable = regs.stencil_enable; } + + SetupCachedFramebuffer(fbkey, current_state); + SyncViewport(current_state); } 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 { struct ScreenInfo; struct DrawParameters; +struct FramebufferCacheKey; class RasterizerOpenGL : public VideoCore::RasterizerInterface { public: @@ -195,11 +196,12 @@ private: OGLVertexArray> vertex_array_cache; + std::map<FramebufferCacheKey, OGLFramebuffer> framebuffer_cache; + std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers; static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; OGLBufferCache buffer_cache; - OGLFramebuffer framebuffer; PrimitiveAssembler primitive_assembler{buffer_cache}; GLint uniform_buffer_alignment; @@ -214,6 +216,8 @@ private: void SetupShaders(GLenum primitive_mode); + void SetupCachedFramebuffer(const FramebufferCacheKey& fbkey, OpenGLState& current_state); + enum class AccelDraw { Disabled, Arrays, Indexed }; AccelDraw accelerate_draw = AccelDraw::Disabled; 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: using std::runtime_error::runtime_error; }; +/// Generates code to use for a swizzle operation. +static std::string GetSwizzle(u64 elem) { + ASSERT(elem <= 3); + std::string swizzle = "."; + swizzle += "xyzw"[elem]; + return swizzle; +} + /// Translate topology static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { switch (topology) { @@ -1004,14 +1012,6 @@ private: } } - /// Generates code to use for a swizzle operation. - static std::string GetSwizzle(u64 elem) { - ASSERT(elem <= 3); - std::string swizzle = "."; - swizzle += "xyzw"[elem]; - return swizzle; - } - ShaderWriter& shader; ShaderWriter& declarations; std::vector<GLSLRegister> regs; @@ -1343,7 +1343,7 @@ private: regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); } - void WriteTexsInstruction(const Instruction& instr, const std::string& texture) { + void WriteTexsInstructionFloat(const Instruction& instr, const std::string& texture) { // TEXS has two destination registers and a swizzle. The first two elements in the swizzle // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 @@ -1368,6 +1368,38 @@ private: } } + void WriteTexsInstructionHalfFloat(const Instruction& instr, const std::string& texture) { + // TEXS.F16 destionation registers are packed in two registers in pairs (just like any half + // float instruction). + + std::array<std::string, 4> components; + u32 written_components = 0; + + for (u32 component = 0; component < 4; ++component) { + if (!instr.texs.IsComponentEnabled(component)) + continue; + components[written_components++] = texture + GetSwizzle(component); + } + if (written_components == 0) + return; + + const auto BuildComponent = [&](std::string low, std::string high, bool high_enabled) { + return "vec2(" + low + ", " + (high_enabled ? high : "0") + ')'; + }; + + regs.SetRegisterToHalfFloat( + instr.gpr0, 0, BuildComponent(components[0], components[1], written_components > 1), + Tegra::Shader::HalfMerge::H0_H1, 1, 1); + + if (written_components > 2) { + ASSERT(instr.texs.HasTwoDestinations()); + regs.SetRegisterToHalfFloat( + instr.gpr28, 0, + BuildComponent(components[2], components[3], written_components > 3), + Tegra::Shader::HalfMerge::H0_H1, 1, 1); + } + } + static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { switch (texture_type) { case Tegra::Shader::TextureType::Texture1D: @@ -2766,24 +2798,27 @@ private: const bool depth_compare = instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); const auto process_mode = instr.texs.GetTextureProcessMode(); + UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), "NODEP is not implemented"); const auto scope = shader.Scope(); - const auto [coord, texture] = + auto [coord, texture] = GetTEXSCode(instr, texture_type, process_mode, depth_compare, is_array); shader.AddLine(coord); - if (!depth_compare) { - shader.AddLine("vec4 texture_tmp = " + texture + ';'); + if (depth_compare) { + texture = "vec4(" + texture + ')'; + } + shader.AddLine("vec4 texture_tmp = " + texture + ';'); + if (instr.texs.fp32_flag) { + WriteTexsInstructionFloat(instr, "texture_tmp"); } else { - shader.AddLine("vec4 texture_tmp = vec4(" + texture + ");"); + WriteTexsInstructionHalfFloat(instr, "texture_tmp"); } - - WriteTexsInstruction(instr, "texture_tmp"); break; } case OpCode::Id::TLDS: { @@ -2842,7 +2877,7 @@ private: } }(); - WriteTexsInstruction(instr, texture); + WriteTexsInstructionFloat(instr, texture); break; } case OpCode::Id::TLD4: { @@ -2940,7 +2975,8 @@ private: if (depth_compare) { texture = "vec4(" + texture + ')'; } - WriteTexsInstruction(instr, texture); + + WriteTexsInstructionFloat(instr, texture); break; } case OpCode::Id::TXQ: { diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 83ebbd1fe..c26161169 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -206,60 +206,57 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default void Config::ReadPlayerValues() { for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { - Settings::values.players[p].connected = - qt_config->value(QString("player_%1_connected").arg(p), false).toBool(); + auto& player = Settings::values.players[p]; - Settings::values.players[p].type = static_cast<Settings::ControllerType>( + player.connected = qt_config->value(QString("player_%1_connected").arg(p), false).toBool(); + + player.type = static_cast<Settings::ControllerType>( qt_config ->value(QString("player_%1_type").arg(p), static_cast<u8>(Settings::ControllerType::DualJoycon)) .toUInt()); - Settings::values.players[p].body_color_left = - qt_config - ->value(QString("player_%1_body_color_left").arg(p), - Settings::JOYCON_BODY_NEON_BLUE) - .toUInt(); - Settings::values.players[p].body_color_right = - qt_config - ->value(QString("player_%1_body_color_right").arg(p), - Settings::JOYCON_BODY_NEON_RED) - .toUInt(); - Settings::values.players[p].button_color_left = - qt_config - ->value(QString("player_%1_button_color_left").arg(p), - Settings::JOYCON_BUTTONS_NEON_BLUE) - .toUInt(); - Settings::values.players[p].button_color_right = - qt_config - ->value(QString("player_%1_button_color_right").arg(p), - Settings::JOYCON_BUTTONS_NEON_RED) - .toUInt(); + player.body_color_left = qt_config + ->value(QString("player_%1_body_color_left").arg(p), + Settings::JOYCON_BODY_NEON_BLUE) + .toUInt(); + player.body_color_right = qt_config + ->value(QString("player_%1_body_color_right").arg(p), + Settings::JOYCON_BODY_NEON_RED) + .toUInt(); + player.button_color_left = qt_config + ->value(QString("player_%1_button_color_left").arg(p), + Settings::JOYCON_BUTTONS_NEON_BLUE) + .toUInt(); + player.button_color_right = qt_config + ->value(QString("player_%1_button_color_right").arg(p), + Settings::JOYCON_BUTTONS_NEON_RED) + .toUInt(); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - Settings::values.players[p].buttons[i] = + player.buttons[i] = qt_config ->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i], QString::fromStdString(default_param)) .toString() .toStdString(); - if (Settings::values.players[p].buttons[i].empty()) - Settings::values.players[p].buttons[i] = default_param; + if (player.buttons[i].empty()) + player.buttons[i] = default_param; } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_analogs[i][4], 0.5f); - Settings::values.players[p].analogs[i] = + player.analogs[i] = qt_config ->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param)) .toString() .toStdString(); - if (Settings::values.players[p].analogs[i].empty()) - Settings::values.players[p].analogs[i] = default_param; + if (player.analogs[i].empty()) + player.analogs[i] = default_param; } } @@ -511,30 +508,28 @@ void Config::ReadValues() { } void Config::SavePlayerValues() { - for (int p = 0; p < Settings::values.players.size(); ++p) { - qt_config->setValue(QString("player_%1_connected").arg(p), - Settings::values.players[p].connected); - qt_config->setValue(QString("player_%1_type").arg(p), - static_cast<u8>(Settings::values.players[p].type)); - - qt_config->setValue(QString("player_%1_body_color_left").arg(p), - Settings::values.players[p].body_color_left); - qt_config->setValue(QString("player_%1_body_color_right").arg(p), - Settings::values.players[p].body_color_right); + for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { + const auto& player = Settings::values.players[p]; + + qt_config->setValue(QString("player_%1_connected").arg(p), player.connected); + qt_config->setValue(QString("player_%1_type").arg(p), static_cast<u8>(player.type)); + + qt_config->setValue(QString("player_%1_body_color_left").arg(p), player.body_color_left); + qt_config->setValue(QString("player_%1_body_color_right").arg(p), player.body_color_right); qt_config->setValue(QString("player_%1_button_color_left").arg(p), - Settings::values.players[p].button_color_left); + player.button_color_left); qt_config->setValue(QString("player_%1_button_color_right").arg(p), - Settings::values.players[p].button_color_right); + player.button_color_right); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { qt_config->setValue(QString("player_%1_").arg(p) + QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(Settings::values.players[p].buttons[i])); + QString::fromStdString(player.buttons[i])); } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { qt_config->setValue(QString("player_%1_").arg(p) + QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(Settings::values.players[p].analogs[i])); + QString::fromStdString(player.analogs[i])); } } } diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 9fd074223..20f5e8798 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -99,12 +99,14 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri compatibility = it->second.first; } + const auto file_type = loader.GetFileType(); + const auto file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type)); + QList<QStandardItem*> list{ - new GameListItemPath( - FormatGameName(path), icon, QString::fromStdString(name), - QString::fromStdString(Loader::GetFileTypeString(loader.GetFileType())), program_id), + new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name), + file_type_string, program_id), new GameListItemCompat(compatibility), - new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader.GetFileType()))), + new GameListItem(file_type_string), new GameListItemSize(FileUtil::GetSize(path)), }; @@ -196,12 +198,16 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign const bool is_dir = FileUtil::IsDirectory(physical_name); if (!is_dir && (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { - std::unique_ptr<Loader::AppLoader> loader = - Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); - if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown || - loader->GetFileType() == Loader::FileType::Error) && - !UISettings::values.show_unknown)) + auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); + if (!loader) { return true; + } + + const auto file_type = loader->GetFileType(); + if ((file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) && + !UISettings::values.show_unknown) { + return true; + } std::vector<u8> icon; const auto res1 = loader->ReadIcon(icon); |