diff options
35 files changed, 515 insertions, 201 deletions
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index c8fa912bf..e065e592f 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -8,13 +8,23 @@ namespace FileSys { -const std::array<const char*, 15> LANGUAGE_NAMES = { - "AmericanEnglish", "BritishEnglish", "Japanese", - "French", "German", "LatinAmericanSpanish", - "Spanish", "Italian", "Dutch", - "CanadianFrench", "Portugese", "Russian", - "Korean", "Taiwanese", "Chinese", -}; +const std::array<const char*, 15> LANGUAGE_NAMES{{ + "AmericanEnglish", + "BritishEnglish", + "Japanese", + "French", + "German", + "LatinAmericanSpanish", + "Spanish", + "Italian", + "Dutch", + "CanadianFrench", + "Portuguese", + "Russian", + "Korean", + "Taiwanese", + "Chinese", +}}; std::string LanguageEntry::GetApplicationName() const { return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index a3f8f2f73..07c3af64a 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -381,22 +381,22 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter( return out; } -static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> nsp, const NcaID& id) { - const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false))); +static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) { + const auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false))); if (file == nullptr) return nullptr; return std::make_shared<NCA>(file); } -InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists, +InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_exists, const VfsCopyFunction& copy) { - return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy); + return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); } -InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists, +InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, const VfsCopyFunction& copy) { - const auto& ncas = nsp->GetNCAsCollapsed(); - const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) { + const auto ncas = nsp.GetNCAsCollapsed(); + const auto meta_iter = std::find_if(ncas.begin(), ncas.end(), [](const auto& nca) { return nca->GetType() == NCAContentType::Meta; }); @@ -410,7 +410,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overw const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32); const auto meta_id = Common::HexStringToArray<16>(meta_id_raw); - const auto res = RawInstallNCA(*meta_iter, copy, overwrite_if_exists, meta_id); + const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id); if (res != InstallResult::Success) return res; @@ -422,7 +422,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overw const auto nca = GetNCAFromNSPForID(nsp, record.nca_id); if (nca == nullptr) return InstallResult::ErrorCopyFailed; - const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id); + const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id); if (res2 != InstallResult::Success) return res2; } @@ -431,21 +431,21 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overw return InstallResult::Success; } -InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType type, +InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists, const VfsCopyFunction& copy) { CNMTHeader header{ - nca->GetTitleId(), ///< Title ID - 0, ///< Ignore/Default title version - type, ///< Type - {}, ///< Padding - 0x10, ///< Default table offset - 1, ///< 1 Content Entry - 0, ///< No Meta Entries - {}, ///< Padding + nca.GetTitleId(), ///< Title ID + 0, ///< Ignore/Default title version + type, ///< Type + {}, ///< Padding + 0x10, ///< Default table offset + 1, ///< 1 Content Entry + 0, ///< No Meta Entries + {}, ///< Padding }; OptionalHeader opt_header{0, 0}; - ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca->GetType()), {}}; - const auto& data = nca->GetBaseFile()->ReadBytes(0x100000); + ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}}; + const auto& data = nca.GetBaseFile()->ReadBytes(0x100000); mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0); memcpy(&c_rec.nca_id, &c_rec.hash, 16); const CNMT new_cnmt(header, opt_header, {c_rec}, {}); @@ -454,10 +454,10 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); } -InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy, +InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, bool overwrite_if_exists, std::optional<NcaID> override_id) { - const auto in = nca->GetBaseFile(); + const auto in = nca.GetBaseFile(); Core::Crypto::SHA256Hash hash{}; // Calculate NcaID diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 6b89db8de..3b77af4e0 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -6,7 +6,6 @@ #include <array> #include <functional> -#include <map> #include <memory> #include <string> #include <vector> @@ -104,17 +103,16 @@ public: // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure // there is a meta NCA and all of them are accessible. - InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false, + InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, const VfsCopyFunction& copy = &VfsRawCopy); - InstallResult InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists = false, + InstallResult InstallEntry(const NSP& nsp, bool overwrite_if_exists = false, const VfsCopyFunction& copy = &VfsRawCopy); // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a // dir inside the NAND called 'yuzu_meta' and store the raw CNMT there. // TODO(DarkLordZach): Author real meta-type NCAs and install those. - InstallResult InstallEntry(std::shared_ptr<NCA> nca, TitleType type, - bool overwrite_if_exists = false, + InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, const VfsCopyFunction& copy = &VfsRawCopy); private: @@ -128,7 +126,7 @@ private: std::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const; VirtualFile GetFileAtID(NcaID id) const; VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const; - InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy, + InstallResult RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, bool overwrite_if_exists, std::optional<NcaID> override_id = {}); bool RawInstallYuzuMeta(const CNMT& cnmt); diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 687dea409..e6b5171ee 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -282,7 +282,7 @@ static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr) if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val; } else if (id == FPCR_REGISTER) { - thread_context.fpcr = val[0]; + thread_context.fpcr = static_cast<u32>(val[0]); } } diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index bec065543..59dc11c22 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -14,7 +14,7 @@ namespace Kernel { class KernelCore; -enum class ResourceType { +enum class ResourceType : u32 { PhysicalMemory, Threads, Events, @@ -25,6 +25,10 @@ enum class ResourceType { ResourceTypeCount }; +constexpr bool IsValidResourceType(ResourceType type) { + return type < ResourceType::ResourceTypeCount; +} + class ResourceLimit final : public Object { public: /** diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f287f7c97..1f19d5576 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -105,6 +105,38 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add return RESULT_SUCCESS; } + +enum class ResourceLimitValueType { + CurrentValue, + LimitValue, +}; + +ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_type, + ResourceLimitValueType value_type) { + const auto type = static_cast<ResourceType>(resource_type); + if (!IsValidResourceType(type)) { + LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); + return ERR_INVALID_ENUM_VALUE; + } + + const auto& kernel = Core::System::GetInstance().Kernel(); + const auto* const current_process = kernel.CurrentProcess(); + ASSERT(current_process != nullptr); + + const auto resource_limit_object = + current_process->GetHandleTable().Get<ResourceLimit>(resource_limit); + if (!resource_limit_object) { + LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}", + resource_limit); + return ERR_INVALID_HANDLE; + } + + if (value_type == ResourceLimitValueType::CurrentValue) { + return MakeResult(resource_limit_object->GetCurrentResourceValue(type)); + } + + return MakeResult(resource_limit_object->GetMaxResourceValue(type)); +} } // Anonymous namespace /// Set the process heap to a given Size. It can both extend and shrink the heap. @@ -1346,6 +1378,87 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { return RESULT_SUCCESS; } +static ResultCode CreateResourceLimit(Handle* out_handle) { + LOG_DEBUG(Kernel_SVC, "called"); + + auto& kernel = Core::System::GetInstance().Kernel(); + auto resource_limit = ResourceLimit::Create(kernel); + + auto* const current_process = kernel.CurrentProcess(); + ASSERT(current_process != nullptr); + + const auto handle = current_process->GetHandleTable().Create(std::move(resource_limit)); + if (handle.Failed()) { + return handle.Code(); + } + + *out_handle = *handle; + return RESULT_SUCCESS; +} + +static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_limit, + u32 resource_type) { + LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); + + const auto limit_value = RetrieveResourceLimitValue(resource_limit, resource_type, + ResourceLimitValueType::LimitValue); + if (limit_value.Failed()) { + return limit_value.Code(); + } + + *out_value = static_cast<u64>(*limit_value); + return RESULT_SUCCESS; +} + +static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_limit, + u32 resource_type) { + LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); + + const auto current_value = RetrieveResourceLimitValue(resource_limit, resource_type, + ResourceLimitValueType::CurrentValue); + if (current_value.Failed()) { + return current_value.Code(); + } + + *out_value = static_cast<u64>(*current_value); + return RESULT_SUCCESS; +} + +static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource_type, u64 value) { + LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit, + resource_type, value); + + const auto type = static_cast<ResourceType>(resource_type); + if (!IsValidResourceType(type)) { + LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); + return ERR_INVALID_ENUM_VALUE; + } + + auto& kernel = Core::System::GetInstance().Kernel(); + auto* const current_process = kernel.CurrentProcess(); + ASSERT(current_process != nullptr); + + auto resource_limit_object = + current_process->GetHandleTable().Get<ResourceLimit>(resource_limit); + if (!resource_limit_object) { + LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}", + resource_limit); + return ERR_INVALID_HANDLE; + } + + const auto set_result = resource_limit_object->SetLimitValue(type, static_cast<s64>(value)); + if (set_result.IsError()) { + LOG_ERROR( + Kernel_SVC, + "Attempted to lower resource limit ({}) for category '{}' below its current value ({})", + resource_limit_object->GetMaxResourceValue(type), resource_type, + resource_limit_object->GetCurrentResourceValue(type)); + return set_result; + } + + return RESULT_SUCCESS; +} + namespace { struct FunctionDef { using Func = void(); @@ -1405,8 +1518,8 @@ static const FunctionDef SVC_Table[] = { {0x2D, nullptr, "UnmapPhysicalMemory"}, {0x2E, nullptr, "GetFutureThreadInfo"}, {0x2F, nullptr, "GetLastThreadInfo"}, - {0x30, nullptr, "GetResourceLimitLimitValue"}, - {0x31, nullptr, "GetResourceLimitCurrentValue"}, + {0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"}, + {0x31, SvcWrap<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"}, {0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"}, {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"}, {0x34, SvcWrap<WaitForAddress>, "WaitForAddress"}, @@ -1482,8 +1595,8 @@ static const FunctionDef SVC_Table[] = { {0x7A, nullptr, "StartProcess"}, {0x7B, nullptr, "TerminateProcess"}, {0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"}, - {0x7D, nullptr, "CreateResourceLimit"}, - {0x7E, nullptr, "SetResourceLimitLimitValue"}, + {0x7D, SvcWrap<CreateResourceLimit>, "CreateResourceLimit"}, + {0x7E, SvcWrap<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"}, {0x7F, nullptr, "CallSecureMonitor"}, }; diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 233a99fb0..fa1116624 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -43,6 +43,14 @@ void SvcWrap() { FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw); } +template <ResultCode func(u32*)> +void SvcWrap() { + u32 param = 0; + const u32 retval = func(¶m).raw; + Core::CurrentArmInterface().SetReg(1, param); + FuncReturn(retval); +} + template <ResultCode func(u32*, u32)> void SvcWrap() { u32 param_1 = 0; diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index c629f9357..9521431bb 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -21,17 +21,6 @@ namespace Service::Account { -// TODO: RE this structure -struct UserData { - INSERT_PADDING_WORDS(1); - u32 icon_id; - u8 bg_color_id; - INSERT_PADDING_BYTES(0x7); - INSERT_PADDING_BYTES(0x10); - INSERT_PADDING_BYTES(0x60); -}; -static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); - // Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg // used as a backup should the one on disk not exist constexpr u32 backup_jpeg_size = 107; @@ -72,9 +61,11 @@ private: void Get(Kernel::HLERequestContext& ctx) { LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); ProfileBase profile_base{}; - std::array<u8, MAX_DATA> data{}; + ProfileData data{}; if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { - ctx.WriteBuffer(data); + std::array<u8, sizeof(ProfileData)> raw_data; + std::memcpy(raw_data.data(), &data, sizeof(ProfileData)); + ctx.WriteBuffer(raw_data); IPC::ResponseBuilder rb{ctx, 16}; rb.Push(RESULT_SUCCESS); rb.PushRaw(profile_base); diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 968263846..1316d0b07 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -18,7 +18,7 @@ struct UserRaw { UUID uuid2; u64 timestamp; ProfileUsername username; - INSERT_PADDING_BYTES(0x80); + ProfileData extra_data; }; static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size."); @@ -346,7 +346,7 @@ void ProfileManager::ParseUserSaveFile() { continue; } - AddUser({user.uuid, user.username, user.timestamp, {}, false}); + AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false}); } std::stable_partition(profiles.begin(), profiles.end(), @@ -361,6 +361,7 @@ void ProfileManager::WriteUserSaveFile() { raw.users[i].uuid2 = profiles[i].user_uuid; raw.users[i].uuid = profiles[i].user_uuid; raw.users[i].timestamp = profiles[i].creation_time; + raw.users[i].extra_data = profiles[i].data; } const auto raw_path = diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index d2d8e6c6b..c4ce2e0b3 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -13,7 +13,6 @@ namespace Service::Account { constexpr std::size_t MAX_USERS = 8; -constexpr std::size_t MAX_DATA = 128; constexpr u128 INVALID_UUID{{0, 0}}; struct UUID { @@ -50,9 +49,20 @@ static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); constexpr std::size_t profile_username_size = 32; using ProfileUsername = std::array<u8, profile_username_size>; -using ProfileData = std::array<u8, MAX_DATA>; using UserIDArray = std::array<UUID, MAX_USERS>; +/// Contains extra data related to a user. +/// TODO: RE this structure +struct ProfileData { + INSERT_PADDING_WORDS(1); + u32 icon_id; + u8 bg_color_id; + INSERT_PADDING_BYTES(0x7); + INSERT_PADDING_BYTES(0x10); + INSERT_PADDING_BYTES(0x60); +}; +static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size"); + /// This holds general information about a users profile. This is where we store all the information /// based on a specific user struct ProfileInfo { diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index ea8057b80..abff6544d 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -80,9 +80,9 @@ public: struct LedPattern { explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { position1.Assign(light1); - position1.Assign(light2); - position1.Assign(light3); - position1.Assign(light4); + position2.Assign(light2); + position3.Assign(light3); + position4.Assign(light4); } union { u64 raw{}; diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 28e8c13aa..8b9c548cc 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -34,6 +34,9 @@ MICROPROFILE_DEFINE(ProcessCommandLists, "GPU", "Execute command buffer", MP_RGB void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) { MICROPROFILE_SCOPE(ProcessCommandLists); + // On entering GPU code, assume all memory may be touched by the ARM core. + maxwell_3d->dirty_flags.OnMemoryWrite(); + auto WriteReg = [this](u32 method, u32 subchannel, u32 value, u32 remaining_params) { LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {} value " diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index ee8ebf678..e7721a2be 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/core.h" #include "core/memory.h" #include "video_core/engines/fermi_2d.h" +#include "video_core/engines/maxwell_3d.h" #include "video_core/rasterizer_interface.h" #include "video_core/textures/decoders.h" @@ -47,6 +49,9 @@ void Fermi2D::HandleSurfaceCopy() { u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format); if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) { + // All copies here update the main memory, so mark all rasterizer states as invalid. + Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); + rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height); // We have to invalidate the destination region to evict any outdated surfaces from the // cache. We do this before actually writing the new data because the destination address diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 585290d9f..2adbc9eaf 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp @@ -3,8 +3,10 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "core/core.h" #include "core/memory.h" #include "video_core/engines/kepler_memory.h" +#include "video_core/engines/maxwell_3d.h" #include "video_core/rasterizer_interface.h" namespace Tegra::Engines { @@ -47,6 +49,7 @@ void KeplerMemory::ProcessData(u32 data) { rasterizer.InvalidateRegion(dest_address, sizeof(u32)); Memory::Write32(dest_address, data); + Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); state.write_offset++; } diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 2bc534be3..f0a5470b9 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -135,10 +135,24 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { if (regs.reg_array[method] != value) { regs.reg_array[method] = value; + // Vertex format if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) && method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) { dirty_flags.vertex_attrib_format = true; } + + // Vertex buffer + if (method >= MAXWELL3D_REG_INDEX(vertex_array) && + method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * 32) { + dirty_flags.vertex_array |= 1u << ((method - MAXWELL3D_REG_INDEX(vertex_array)) >> 2); + } else if (method >= MAXWELL3D_REG_INDEX(vertex_array_limit) && + method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * 32) { + dirty_flags.vertex_array |= + 1u << ((method - MAXWELL3D_REG_INDEX(vertex_array_limit)) >> 1); + } else if (method >= MAXWELL3D_REG_INDEX(instanced_arrays) && + method < MAXWELL3D_REG_INDEX(instanced_arrays) + 32) { + dirty_flags.vertex_array |= 1u << (method - MAXWELL3D_REG_INDEX(instanced_arrays)); + } } switch (method) { @@ -270,6 +284,7 @@ void Maxwell3D::ProcessQueryGet() { query_result.timestamp = CoreTiming::GetTicks(); Memory::WriteBlock(*address, &query_result, sizeof(query_result)); } + dirty_flags.OnMemoryWrite(); break; } default: @@ -346,6 +361,7 @@ void Maxwell3D::ProcessCBData(u32 value) { memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos); Memory::Write32(*address, value); + dirty_flags.OnMemoryWrite(); // Increment the current buffer position. regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4; diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 4f137e693..9324d9710 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -590,10 +590,18 @@ public: float clear_color[4]; float clear_depth; + INSERT_PADDING_WORDS(0x3); + s32 clear_stencil; - INSERT_PADDING_WORDS(0x17); + INSERT_PADDING_WORDS(0x7); + + u32 polygon_offset_point_enable; + u32 polygon_offset_line_enable; + u32 polygon_offset_fill_enable; + + INSERT_PADDING_WORDS(0xD); std::array<ScissorTest, NumViewports> scissor_test; @@ -728,6 +736,7 @@ public: u32 frag_color_clamp; union { + BitField<0, 1, u32> y_negate; BitField<4, 1, u32> triangle_rast_flip; } screen_y_control; @@ -761,7 +770,11 @@ public: } } tsc; - INSERT_PADDING_WORDS(0x3); + INSERT_PADDING_WORDS(0x1); + + float polygon_offset_factor; + + INSERT_PADDING_WORDS(0x1); struct { u32 tic_address_high; @@ -786,7 +799,9 @@ public: u32 framebuffer_srgb; - INSERT_PADDING_WORDS(0x12); + float polygon_offset_units; + + INSERT_PADDING_WORDS(0x11); union { BitField<2, 1, u32> coord_origin; @@ -863,7 +878,9 @@ public: INSERT_PADDING_WORDS(0x7); - INSERT_PADDING_WORDS(0x20); + INSERT_PADDING_WORDS(0x1F); + + float polygon_offset_clamp; struct { u32 is_instanced[NumVertexArrays]; @@ -879,7 +896,13 @@ public: Cull cull; - INSERT_PADDING_WORDS(0x28); + u32 pixel_center_integer; + + INSERT_PADDING_WORDS(0x1); + + u32 viewport_transform_enabled; + + INSERT_PADDING_WORDS(0x25); struct { u32 enable; @@ -1044,6 +1067,11 @@ public: struct DirtyFlags { bool vertex_attrib_format = true; + u32 vertex_array = 0xFFFFFFFF; + + void OnMemoryWrite() { + vertex_array = 0xFFFFFFFF; + } }; DirtyFlags dirty_flags; @@ -1136,6 +1164,9 @@ ASSERT_REG_POSITION(vertex_buffer, 0x35D); ASSERT_REG_POSITION(clear_color[0], 0x360); ASSERT_REG_POSITION(clear_depth, 0x364); ASSERT_REG_POSITION(clear_stencil, 0x368); +ASSERT_REG_POSITION(polygon_offset_point_enable, 0x370); +ASSERT_REG_POSITION(polygon_offset_line_enable, 0x371); +ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372); ASSERT_REG_POSITION(scissor_test, 0x380); ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); @@ -1174,6 +1205,7 @@ ASSERT_REG_POSITION(point_size, 0x546); ASSERT_REG_POSITION(zeta_enable, 0x54E); ASSERT_REG_POSITION(multisample_control, 0x54F); ASSERT_REG_POSITION(tsc, 0x557); +ASSERT_REG_POSITION(polygon_offset_factor, 0x55b); ASSERT_REG_POSITION(tic, 0x55D); ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); ASSERT_REG_POSITION(stencil_back_op_fail, 0x566); @@ -1181,13 +1213,17 @@ ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567); ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); ASSERT_REG_POSITION(stencil_back_func_func, 0x569); ASSERT_REG_POSITION(framebuffer_srgb, 0x56E); +ASSERT_REG_POSITION(polygon_offset_units, 0x56F); ASSERT_REG_POSITION(point_coord_replace, 0x581); ASSERT_REG_POSITION(code_address, 0x582); ASSERT_REG_POSITION(draw, 0x585); ASSERT_REG_POSITION(primitive_restart, 0x591); ASSERT_REG_POSITION(index_array, 0x5F2); +ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F); ASSERT_REG_POSITION(instanced_arrays, 0x620); ASSERT_REG_POSITION(cull, 0x646); +ASSERT_REG_POSITION(pixel_center_integer, 0x649); +ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B); ASSERT_REG_POSITION(logic_op, 0x671); ASSERT_REG_POSITION(clear_buffers, 0x674); ASSERT_REG_POSITION(color_mask, 0x680); diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index b8a78cf82..a34e884fe 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -2,7 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/core.h" #include "core/memory.h" +#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_interface.h" #include "video_core/textures/decoders.h" @@ -54,6 +56,9 @@ void MaxwellDMA::HandleCopy() { return; } + // All copies here update the main memory, so mark all rasterizer states as invalid. + Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); + if (regs.exec.is_dst_linear && regs.exec.is_src_linear) { // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count, diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp index 1cf27ee25..a310491a8 100644 --- a/src/video_core/morton.cpp +++ b/src/video_core/morton.cpp @@ -184,13 +184,14 @@ static constexpr ConversionArray linear_to_morton_fns = { // clang-format on }; -constexpr MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) { +static MortonCopyFn GetSwizzleFunction(MortonSwizzleMode mode, Surface::PixelFormat format) { switch (mode) { case MortonSwizzleMode::MortonToLinear: return morton_to_linear_fns[static_cast<std::size_t>(format)]; case MortonSwizzleMode::LinearToMorton: return linear_to_morton_fns[static_cast<std::size_t>(format)]; } + UNREACHABLE(); } /// 8x8 Z-Order coordinate from 2D coordinates diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 075192c3f..46a6c0308 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -76,7 +76,7 @@ std::tuple<u8*, GLintptr> OGLBufferCache::ReserveMemory(std::size_t size, std::s return std::make_tuple(uploaded_ptr, uploaded_offset); } -void OGLBufferCache::Map(std::size_t max_size) { +bool OGLBufferCache::Map(std::size_t max_size) { bool invalidate; std::tie(buffer_ptr, buffer_offset_base, invalidate) = stream_buffer.Map(static_cast<GLsizeiptr>(max_size), 4); @@ -85,6 +85,7 @@ void OGLBufferCache::Map(std::size_t max_size) { if (invalidate) { InvalidateAll(); } + return invalidate; } void OGLBufferCache::Unmap() { diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 91fca3f6c..c11acfb79 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -50,7 +50,7 @@ public: /// Reserves memory to be used by host's CPU. Returns mapped address and offset. std::tuple<u8*, GLintptr> ReserveMemory(std::size_t size, std::size_t alignment = 4); - void Map(std::size_t max_size); + bool Map(std::size_t max_size); void Unmap(); GLuint GetHandle() const; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 630a58e49..82b7a0649 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -176,15 +176,25 @@ void RasterizerOpenGL::SetupVertexFormat() { } state.draw.vertex_array = VAO.handle; state.ApplyVertexBufferState(); + + // Rebinding the VAO invalidates the vertex buffer bindings. + gpu.dirty_flags.vertex_array = 0xFFFFFFFF; } void RasterizerOpenGL::SetupVertexBuffer() { - MICROPROFILE_SCOPE(OpenGL_VB); - const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); + auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); const auto& regs = gpu.regs; + if (!gpu.dirty_flags.vertex_array) + return; + + MICROPROFILE_SCOPE(OpenGL_VB); + // Upload all guest vertex arrays sequentially to our buffer for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { + if (~gpu.dirty_flags.vertex_array & (1u << index)) + continue; + const auto& vertex_array = regs.vertex_array[index]; if (!vertex_array.IsEnabled()) continue; @@ -211,6 +221,8 @@ void RasterizerOpenGL::SetupVertexBuffer() { // Implicit set by glBindVertexBuffer. Stupid glstate handling... state.draw.vertex_buffer = buffer_cache.GetHandle(); + + gpu.dirty_flags.vertex_array = 0; } DrawParameters RasterizerOpenGL::SetupDraw() { @@ -600,7 +612,7 @@ void RasterizerOpenGL::DrawArrays() { return; MICROPROFILE_SCOPE(OpenGL_Drawing); - const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); + auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); const auto& regs = gpu.regs; ScopeAcquireGLContext acquire_context{emu_window}; @@ -620,7 +632,7 @@ void RasterizerOpenGL::DrawArrays() { SyncTransformFeedback(); SyncPointState(); CheckAlphaTests(); - + SyncPolygonOffset(); // TODO(bunnei): Sync framebuffer_scale uniform here // TODO(bunnei): Sync scissorbox uniform(s) here @@ -653,7 +665,11 @@ void RasterizerOpenGL::DrawArrays() { // Add space for at least 18 constant buffers buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); - buffer_cache.Map(buffer_size); + bool invalidate = buffer_cache.Map(buffer_size); + if (invalidate) { + // As all cached buffers are invalidated, we need to recheck their state. + gpu.dirty_flags.vertex_array = 0xFFFFFFFF; + } SetupVertexFormat(); SetupVertexBuffer(); @@ -969,13 +985,25 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; - for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) { - const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; + const bool geometry_shaders_enabled = + regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry)); + const std::size_t viewport_count = + geometry_shaders_enabled ? Tegra::Engines::Maxwell3D::Regs::NumViewports : 1; + for (std::size_t i = 0; i < viewport_count; i++) { auto& viewport = current_state.viewports[i]; - viewport.x = viewport_rect.left; - viewport.y = viewport_rect.bottom; - viewport.width = viewport_rect.GetWidth(); - viewport.height = viewport_rect.GetHeight(); + const auto& src = regs.viewports[i]; + if (regs.viewport_transform_enabled) { + const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; + viewport.x = viewport_rect.left; + viewport.y = viewport_rect.bottom; + viewport.width = viewport_rect.GetWidth(); + viewport.height = viewport_rect.GetHeight(); + } else { + viewport.x = src.x; + viewport.y = src.y; + viewport.width = src.width; + viewport.height = src.height; + } viewport.depth_range_far = regs.viewports[i].depth_range_far; viewport.depth_range_near = regs.viewports[i].depth_range_near; } @@ -1149,7 +1177,11 @@ void RasterizerOpenGL::SyncLogicOpState() { void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; - for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) { + const bool geometry_shaders_enabled = + regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry)); + const std::size_t viewport_count = + geometry_shaders_enabled ? Tegra::Engines::Maxwell3D::Regs::NumViewports : 1; + for (std::size_t i = 0; i < viewport_count; i++) { const auto& src = regs.scissor_test[i]; auto& dst = current_state.viewports[i].scissor; dst.enabled = (src.enable != 0); @@ -1179,6 +1211,16 @@ void RasterizerOpenGL::SyncPointState() { state.point.size = regs.point_size; } +void RasterizerOpenGL::SyncPolygonOffset() { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0; + state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0; + state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0; + state.polygon_offset.units = regs.polygon_offset_units; + state.polygon_offset.factor = regs.polygon_offset_factor; + state.polygon_offset.clamp = regs.polygon_offset_clamp; +} + void RasterizerOpenGL::CheckAlphaTests() { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index f4354289c..dfb4616f2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -183,6 +183,9 @@ private: /// Syncs Color Mask void SyncColorMask(); + /// Syncs the polygon offsets + void SyncPolygonOffset(); + /// Check asserts for alpha testing. void CheckAlphaTests(); diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 05fe2d370..0c4524d5c 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -520,7 +520,7 @@ public: switch (attribute) { case Attribute::Index::ClipDistances0123: case Attribute::Index::ClipDistances4567: { - const u64 index = attribute == Attribute::Index::ClipDistances4567 ? 4 : 0 + elem; + const u64 index = (attribute == Attribute::Index::ClipDistances4567 ? 4 : 0) + elem; UNIMPLEMENTED_IF_MSG( ((header.vtg.clip_distances >> index) & 1) == 0, "Shader is setting gl_ClipDistance{} without enabling it in the header", index); @@ -867,7 +867,8 @@ private: // vertex shader, and what's the value of the fourth element when inside a Tess Eval // shader. ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex); - return "vec4(0, 0, uintBitsToFloat(instance_id.x), uintBitsToFloat(gl_VertexID))"; + // Config pack's first value is instance_id. + return "vec4(0, 0, uintBitsToFloat(config_pack[0]), uintBitsToFloat(gl_VertexID))"; case Attribute::Index::FrontFacing: // TODO(Subv): Find out what the values are for the other elements. ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); @@ -3400,6 +3401,10 @@ private: regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1, 1); } + if (instr.generates_cc.Value() != 0) { + regs.SetInternalFlag(InternalFlag::ZeroFlag, predicate); + LOG_WARNING(HW_GPU, "FSET Condition Code is incomplete"); + } break; } case OpCode::Type::IntegerSet: { @@ -3653,6 +3658,11 @@ private: regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1); break; } + case Tegra::Shader::SystemVariable::Ydirection: { + // Config pack's third value is Y_NEGATE's state. + regs.SetRegisterToFloat(instr.gpr0, 0, "uintBitsToFloat(config_pack[2])", 1, 1); + break; + } default: { UNIMPLEMENTED_MSG("Unhandled system move: {}", static_cast<u32>(instr.sys20.Value())); @@ -3676,11 +3686,17 @@ private: "BRA with constant buffers are not implemented"); const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; - UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, - "BRA condition code used: {}", static_cast<u32>(cc)); - const u32 target = offset + instr.bra.GetBranchTarget(); - shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); + if (cc != Tegra::Shader::ConditionCode::T) { + const std::string condition_code = regs.GetConditionCode(cc); + shader.AddLine("if (" + condition_code + "){"); + shader.scope++; + shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); + shader.scope--; + shader.AddLine('}'); + } else { + shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); + } break; } case OpCode::Id::IPA: { diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index eea090e52..23ed91e27 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -24,8 +24,7 @@ layout (location = 0) out vec4 position; layout(std140) uniform vs_config { vec4 viewport_flip; - uvec4 instance_id; - uvec4 flip_stage; + uvec4 config_pack; // instance_id, flip_stage, y_direction, padding uvec4 alpha_test; }; )"; @@ -63,7 +62,8 @@ void main() { out += R"( // Check if the flip stage is VertexB - if (flip_stage[0] == 1) { + // Config pack's second value is flip_stage + if (config_pack[1] == 1) { // Viewport can be flipped, which is unsupported by glViewport position.xy *= viewport_flip.xy; } @@ -71,7 +71,7 @@ void main() { // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0 // For now, this is here to bring order in lieu of proper emulation - if (flip_stage[0] == 1) { + if (config_pack[1] == 1) { position.w = 1.0; } } @@ -101,8 +101,7 @@ layout (location = 0) out vec4 position; layout (std140) uniform gs_config { vec4 viewport_flip; - uvec4 instance_id; - uvec4 flip_stage; + uvec4 config_pack; // instance_id, flip_stage, y_direction, padding uvec4 alpha_test; }; @@ -139,8 +138,7 @@ layout (location = 0) in vec4 position; layout (std140) uniform fs_config { vec4 viewport_flip; - uvec4 instance_id; - uvec4 flip_stage; + uvec4 config_pack; // instance_id, flip_stage, y_direction, padding uvec4 alpha_test; }; diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 8b8869ecb..6a30c28d2 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -27,16 +27,18 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh alpha_test.func = func; alpha_test.ref = regs.alpha_test_ref; - // We only assign the instance to the first component of the vector, the rest is just padding. - instance_id[0] = state.current_instance; + instance_id = state.current_instance; // Assign in which stage the position has to be flipped // (the last stage before the fragment shader). if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) { - flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); + flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry); } else { - flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); + flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB); } + + // Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value. + y_direction = regs.screen_y_control.y_negate == 0 ? 1.f : -1.f; } } // namespace OpenGL::GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 9a5d7e289..b757f5f44 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -21,8 +21,11 @@ using Tegra::Engines::Maxwell3D; struct MaxwellUniformData { void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); alignas(16) GLvec4 viewport_flip; - alignas(16) GLuvec4 instance_id; - alignas(16) GLuvec4 flip_stage; + struct alignas(16) { + GLuint instance_id; + GLuint flip_stage; + GLfloat y_direction; + }; struct alignas(16) { GLuint enabled; GLuint func; @@ -30,7 +33,7 @@ struct MaxwellUniformData { GLuint padding; } alpha_test; }; -static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect"); +static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect"); static_assert(sizeof(MaxwellUniformData) < 16384, "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 934f4db78..b3bfad6a0 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -92,6 +92,13 @@ OpenGLState::OpenGLState() { point.size = 1; fragment_color_clamp.enabled = false; + + polygon_offset.fill_enable = false; + polygon_offset.line_enable = false; + polygon_offset.point_enable = false; + polygon_offset.factor = 0.0f; + polygon_offset.units = 0.0f; + polygon_offset.clamp = 0.0f; } void OpenGLState::ApplyDefaultState() { @@ -406,6 +413,55 @@ void OpenGLState::ApplyLogicOp() const { } } +void OpenGLState::ApplyPolygonOffset() const { + + const bool fill_enable_changed = + polygon_offset.fill_enable != cur_state.polygon_offset.fill_enable; + const bool line_enable_changed = + polygon_offset.line_enable != cur_state.polygon_offset.line_enable; + const bool point_enable_changed = + polygon_offset.point_enable != cur_state.polygon_offset.point_enable; + const bool factor_changed = polygon_offset.factor != cur_state.polygon_offset.factor; + const bool units_changed = polygon_offset.units != cur_state.polygon_offset.units; + const bool clamp_changed = polygon_offset.clamp != cur_state.polygon_offset.clamp; + + if (fill_enable_changed) { + if (polygon_offset.fill_enable) { + glEnable(GL_POLYGON_OFFSET_FILL); + } else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + } + + if (line_enable_changed) { + if (polygon_offset.line_enable) { + glEnable(GL_POLYGON_OFFSET_LINE); + } else { + glDisable(GL_POLYGON_OFFSET_LINE); + } + } + + if (point_enable_changed) { + if (polygon_offset.point_enable) { + glEnable(GL_POLYGON_OFFSET_POINT); + } else { + glDisable(GL_POLYGON_OFFSET_POINT); + } + } + + if ((polygon_offset.fill_enable || polygon_offset.line_enable || polygon_offset.point_enable) && + (factor_changed || units_changed || clamp_changed)) { + + if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) { + glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp); + } else { + glPolygonOffset(polygon_offset.factor, polygon_offset.units); + UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0, + "Unimplemented Depth polygon offset clamp."); + } + } +} + void OpenGLState::ApplyTextures() const { for (std::size_t i = 0; i < std::size(texture_units); ++i) { const auto& texture_unit = texture_units[i]; @@ -532,6 +588,7 @@ void OpenGLState::Apply() const { ApplyLogicOp(); ApplyTextures(); ApplySamplers(); + ApplyPolygonOffset(); cur_state = *this; } diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 032fc43f0..0bf19ed07 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -176,6 +176,15 @@ public: float size; // GL_POINT_SIZE } point; + struct { + bool point_enable; + bool line_enable; + bool fill_enable; + GLfloat units; + GLfloat factor; + GLfloat clamp; + } polygon_offset; + std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE OpenGLState(); @@ -226,6 +235,7 @@ private: void ApplyLogicOp() const; void ApplyTextures() const; void ApplySamplers() const; + void ApplyPolygonOffset() const; }; } // namespace OpenGL diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 7ee572761..ec46dc4e3 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -4,11 +4,9 @@ #include <algorithm> #include <memory> -#include <utility> -#include <QMenu> -#include <QMessageBox> + #include <QTimer> -#include "common/param_package.h" + #include "configuration/configure_touchscreen_advanced.h" #include "core/core.h" #include "core/hle/service/am/am.h" @@ -16,16 +14,25 @@ #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/sm/sm.h" -#include "input_common/main.h" #include "ui_configure_input.h" #include "ui_configure_input_player.h" -#include "ui_configure_mouse_advanced.h" -#include "ui_configure_touchscreen_advanced.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input.h" #include "yuzu/configuration/configure_input_player.h" #include "yuzu/configuration/configure_mouse_advanced.h" +namespace { +template <typename Dialog, typename... Args> +void CallConfigureDialog(ConfigureInput& parent, Args&&... args) { + parent.applyConfiguration(); + Dialog dialog(&parent, std::forward<Args>(args)...); + + const auto res = dialog.exec(); + if (res == QDialog::Accepted) { + dialog.applyConfiguration(); + } +} +} // Anonymous namespace + ConfigureInput::ConfigureInput(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { ui->setupUi(this); @@ -65,31 +72,20 @@ ConfigureInput::ConfigureInput(QWidget* parent) for (std::size_t i = 0; i < players_configure.size(); ++i) { connect(players_configure[i], &QPushButton::pressed, this, - [this, i]() { CallConfigureDialog<ConfigureInputPlayer>(i, false); }); + [this, i] { CallConfigureDialog<ConfigureInputPlayer>(*this, i, false); }); } connect(ui->handheld_configure, &QPushButton::pressed, this, - [this]() { CallConfigureDialog<ConfigureInputPlayer>(8, false); }); + [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 8, false); }); connect(ui->debug_configure, &QPushButton::pressed, this, - [this]() { CallConfigureDialog<ConfigureInputPlayer>(9, true); }); + [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 9, true); }); connect(ui->mouse_advanced, &QPushButton::pressed, this, - [this]() { CallConfigureDialog<ConfigureMouseAdvanced>(); }); + [this] { CallConfigureDialog<ConfigureMouseAdvanced>(*this); }); connect(ui->touchscreen_advanced, &QPushButton::pressed, this, - [this]() { CallConfigureDialog<ConfigureTouchscreenAdvanced>(); }); -} - -template <typename Dialog, typename... Args> -void ConfigureInput::CallConfigureDialog(Args&&... args) { - this->applyConfiguration(); - Dialog dialog(this, std::forward<Args>(args)...); - - const auto res = dialog.exec(); - if (res == QDialog::Accepted) { - dialog.applyConfiguration(); - } + [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); } void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) { diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index 29a8a03f8..e8723dfcb 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -5,20 +5,12 @@ #pragma once #include <array> -#include <functional> #include <memory> -#include <optional> -#include <string> -#include <unordered_map> #include <QKeyEvent> #include <QWidget> -#include "common/param_package.h" -#include "core/settings.h" -#include "input_common/main.h" #include "ui_configure_input.h" -#include "yuzu/configuration/config.h" class QPushButton; class QString; @@ -40,9 +32,6 @@ public: private: void updateUIEnabled(); - template <typename Dialog, typename... Args> - void CallConfigureDialog(Args&&... args); - void OnDockedModeChanged(bool last_state, bool new_state); /// Load configuration settings. diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index ba6e09368..7dadd83c1 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -25,13 +25,6 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> "modifier", }}; -static void MoveGridElement(QGridLayout* grid, int row_old, int column_old, int row_new, - int column_new) { - const auto item = grid->itemAtPosition(row_old, column_old); - // grid->removeItem(item); - grid->addItem(item, row_new, column_new); -} - static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) { const int index1 = grid->indexOf(item); const int index2 = grid->indexOf(onTopOf); @@ -111,11 +104,10 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string } }; -ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug) - : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), - timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()), - player_index(player_index), debug(debug) { - +ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug) + : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), + debug(debug), timeout_timer(std::make_unique<QTimer>()), + poll_timer(std::make_unique<QTimer>()) { ui->setupUi(this); setFocusPolicy(Qt::ClickFocus); @@ -315,7 +307,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, u8 player_index, boo for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) { connect(controller_color_buttons[i], &QPushButton::clicked, this, - std::bind(&ConfigureInputPlayer::OnControllerButtonClick, this, i)); + [this, i] { OnControllerButtonClick(static_cast<int>(i)); }); } this->loadConfiguration(); diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index b0e5550c5..7a53f6715 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -9,9 +9,10 @@ #include <memory> #include <optional> #include <string> -#include <unordered_map> + #include <QDialog> #include <QKeyEvent> + #include "common/param_package.h" #include "core/settings.h" #include "input_common/main.h" @@ -29,16 +30,39 @@ class ConfigureInputPlayer : public QDialog { Q_OBJECT public: - explicit ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug = false); + explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug = false); ~ConfigureInputPlayer() override; /// Save all button configurations to settings file void applyConfiguration(); private: + void OnControllerButtonClick(int i); + + /// Load configuration settings. + void loadConfiguration(); + /// Restore all buttons to their default values. + void restoreDefaults(); + /// Clear all input configuration + void ClearAll(); + + /// Update UI to reflect current configuration. + void updateButtonLabels(); + + /// Called when the button was pressed. + void handleClick(QPushButton* button, + std::function<void(const Common::ParamPackage&)> new_input_setter, + InputCommon::Polling::DeviceType type); + + /// Finish polling and configure input using the input_setter + void setPollingResult(const Common::ParamPackage& params, bool abort); + + /// Handle key press events. + void keyPressEvent(QKeyEvent* event) override; + std::unique_ptr<Ui::ConfigureInputPlayer> ui; - u8 player_index; + std::size_t player_index; bool debug; std::unique_ptr<QTimer> timeout_timer; @@ -77,27 +101,4 @@ private: std::array<QPushButton*, 4> controller_color_buttons; std::array<QColor, 4> controller_colors; - - void OnControllerButtonClick(int i); - - /// Load configuration settings. - void loadConfiguration(); - /// Restore all buttons to their default values. - void restoreDefaults(); - /// Clear all input configuration - void ClearAll(); - - /// Update UI to reflect current configuration. - void updateButtonLabels(); - - /// Called when the button was pressed. - void handleClick(QPushButton* button, - std::function<void(const Common::ParamPackage&)> new_input_setter, - InputCommon::Polling::DeviceType type); - - /// Finish polling and configure input using the input_setter - void setPollingResult(const Common::ParamPackage& params, bool abort); - - /// Handle key press events. - void keyPressEvent(QKeyEvent* event) override; }; diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp index dab58fbaa..ef857035e 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp @@ -4,11 +4,11 @@ #include <algorithm> #include <memory> -#include <utility> + #include <QKeyEvent> #include <QMenu> -#include <QMessageBox> #include <QTimer> + #include "common/assert.h" #include "common/param_package.h" #include "input_common/main.h" diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h index 218df2bda..e04da4bf2 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.h +++ b/src/yuzu/configuration/configure_mouse_advanced.h @@ -7,7 +7,7 @@ #include <memory> #include <optional> #include <QDialog> -#include <QWidget> + #include "core/settings.h" class QCheckBox; @@ -28,23 +28,6 @@ public: void applyConfiguration(); private: - std::unique_ptr<Ui::ConfigureMouseAdvanced> ui; - - /// This will be the the setting function when an input is awaiting configuration. - std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; - - std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map; - std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param; - - std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; - - std::unique_ptr<QTimer> timeout_timer; - std::unique_ptr<QTimer> poll_timer; - - /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, - /// keyboard events are ignored. - bool want_keyboard_keys = false; - /// Load configuration settings. void loadConfiguration(); /// Restore all buttons to their default values. @@ -65,4 +48,21 @@ private: /// Handle key press events. void keyPressEvent(QKeyEvent* event) override; + + std::unique_ptr<Ui::ConfigureMouseAdvanced> ui; + + /// This will be the the setting function when an input is awaiting configuration. + std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; + + std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map; + std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param; + + std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; + + std::unique_ptr<QTimer> timeout_timer; + std::unique_ptr<QTimer> poll_timer; + + /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, + /// keyboard events are ignored. + bool want_keyboard_keys = false; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 9c6d150a5..93bf117c8 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1105,14 +1105,14 @@ void GMainWindow::OnMenuInstallToNAND() { return; } const auto res = - Service::FileSystem::GetUserNANDContents()->InstallEntry(nsp, false, qt_raw_copy); + Service::FileSystem::GetUserNANDContents()->InstallEntry(*nsp, false, qt_raw_copy); if (res == FileSys::InstallResult::Success) { success(); } else { if (res == FileSys::InstallResult::ErrorAlreadyExists) { if (overwrite()) { const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( - nsp, true, qt_raw_copy); + *nsp, true, qt_raw_copy); if (res2 == FileSys::InstallResult::Success) { success(); } else { @@ -1167,10 +1167,10 @@ void GMainWindow::OnMenuInstallToNAND() { FileSys::InstallResult res; if (index >= static_cast<size_t>(FileSys::TitleType::Application)) { res = Service::FileSystem::GetUserNANDContents()->InstallEntry( - nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); + *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); } else { res = Service::FileSystem::GetSystemNANDContents()->InstallEntry( - nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); + *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); } if (res == FileSys::InstallResult::Success) { @@ -1178,7 +1178,7 @@ void GMainWindow::OnMenuInstallToNAND() { } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { if (overwrite()) { const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( - nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); + *nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); if (res2 == FileSys::InstallResult::Success) { success(); } else { |