diff --git a/CMakeLists.txt b/CMakeLists.txt index 898b48ff0..8d851522f 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -322,6 +322,10 @@ if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) find_package(xbyak 6 CONFIG) endif() +if (ARCHITECTURE_arm64) + find_package(oaknut 2.0.1 CONFIG) +endif() + if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) find_package(dynarmic 6.4.0 CONFIG) endif() diff --git a/README.md b/README.md index b23d30ff0..bffaac0a1 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 4099. +This is the source code for early-access 4100. ## Legal Notice diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index cd88201fe..836aed14f 100755 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -14,16 +14,17 @@ set(BUILD_SHARED_LIBS OFF) # Skip install rules for all externals set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON) -# xbyak +# Xbyak (also used by Dynarmic, so needs to be added first) if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak) add_subdirectory(xbyak) endif() -# Dynarmic +# Oaknut (also used by Dynarmic, so needs to be added first) if (ARCHITECTURE_arm64 AND NOT TARGET merry::oaknut) add_subdirectory(oaknut) endif() +# Dynarmic if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic) set(DYNARMIC_IGNORE_ASSERTS ON) add_subdirectory(dynarmic) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 53137b2e2..6ebb46af7 100755 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -261,7 +261,7 @@ object NativeLibrary { /** * Begins emulation. */ - external fun run(path: String?, programIndex: Int = 0) + external fun run(path: String?, programIndex: Int, frontendInitiated: Boolean) // Surface Handling external fun surfaceChanged(surf: Surface?) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 1f591ced1..937b8faf1 100755 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -927,7 +927,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { emulationThread.join() emulationThread = Thread({ Log.debug("[EmulationFragment] Starting emulation thread.") - NativeLibrary.run(gamePath, programIndex) + NativeLibrary.run(gamePath, programIndex, false) }, "NativeEmulation") emulationThread.start() } @@ -981,7 +981,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { State.STOPPED -> { emulationThread = Thread({ Log.debug("[EmulationFragment] Starting emulation thread.") - NativeLibrary.run(gamePath, programIndex) + NativeLibrary.run(gamePath, programIndex, true) }, "NativeEmulation") emulationThread.start() } diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 4b433b67b..654510129 100755 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -219,7 +219,8 @@ void EmulationSession::SetAppletId(int applet_id) { } Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath, - const std::size_t program_index) { + const std::size_t program_index, + const bool frontend_initiated) { std::scoped_lock lock(m_mutex); // Create the render window. @@ -251,6 +252,8 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string // Load the ROM. Service::AM::FrontendAppletParameters params{ .applet_id = static_cast(m_applet_id), + .launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated + : Service::AM::LaunchType::ApplicationInitiated, .program_index = static_cast(program_index), }; m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params); @@ -447,7 +450,8 @@ u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { } static Core::SystemResultStatus RunEmulation(const std::string& filepath, - const size_t program_index = 0) { + const size_t program_index, + const bool frontend_initiated) { MicroProfileOnThreadCreate("EmuThread"); SCOPE_EXIT({ MicroProfileShutdown(); }); @@ -460,7 +464,8 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath, SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); - jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index); + jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index, + frontend_initiated); if (result != Core::SystemResultStatus::Success) { return result; } @@ -757,10 +762,12 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj } void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path, - jint j_program_index) { + jint j_program_index, + jboolean j_frontend_initiated) { const std::string path = GetJString(env, j_path); - const Core::SystemResultStatus result{RunEmulation(path, j_program_index)}; + const Core::SystemResultStatus result{ + RunEmulation(path, j_program_index, j_frontend_initiated)}; if (result != Core::SystemResultStatus::Success) { env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetExitEmulationActivity(), static_cast(result)); diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index 59342468b..e49d4e015 100755 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h @@ -47,7 +47,8 @@ public: void InitializeSystem(bool reload); void SetAppletId(int applet_id); Core::SystemResultStatus InitializeEmulation(const std::string& filepath, - const std::size_t program_index = 0); + const std::size_t program_index, + const bool frontend_initiated); bool IsHandheldOnly(); void SetDeviceType([[maybe_unused]] int index, int type); diff --git a/src/core/hle/service/cmif_serialization.h b/src/core/hle/service/cmif_serialization.h index 9ee26400d..5eabf51fe 100755 --- a/src/core/hle/service/cmif_serialization.h +++ b/src/core/hle/service/cmif_serialization.h @@ -122,14 +122,14 @@ struct RequestLayout { u32 domain_interface_count; }; -template -constexpr u32 GetArgumentRawDataSize() { +template +constexpr u32 GetInRawDataSize() { if constexpr (ArgIndex >= std::tuple_size_v) { return static_cast(DataOffset); } else { using ArgType = std::tuple_element_t; - if constexpr (ArgumentTraits::Type == Type1 || ArgumentTraits::Type == Type2) { + if constexpr (ArgumentTraits::Type == ArgumentType::InData || ArgumentTraits::Type == ArgumentType::InProcessId) { constexpr size_t ArgAlign = alignof(ArgType); constexpr size_t ArgSize = sizeof(ArgType); @@ -138,9 +138,33 @@ constexpr u32 GetArgumentRawDataSize() { constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); constexpr size_t ArgEnd = ArgOffset + ArgSize; - return GetArgumentRawDataSize(); + return GetInRawDataSize(); } else { - return GetArgumentRawDataSize(); + return GetInRawDataSize(); + } + } +} + +template +constexpr u32 GetOutRawDataSize() { + if constexpr (ArgIndex >= std::tuple_size_v) { + return static_cast(DataOffset); + } else { + using ArgType = std::tuple_element_t; + + if constexpr (ArgumentTraits::Type == ArgumentType::OutData) { + using RawArgType = typename ArgType::Type; + constexpr size_t ArgAlign = alignof(RawArgType); + constexpr size_t ArgSize = sizeof(RawArgType); + + static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + return GetOutRawDataSize(); + } else { + return GetOutRawDataSize(); } } } @@ -165,7 +189,7 @@ constexpr RequestLayout GetNonDomainReplyInLayout() { return RequestLayout{ .copy_handle_count = GetArgumentTypeCount(), .move_handle_count = 0, - .cmif_raw_data_size = GetArgumentRawDataSize(), + .cmif_raw_data_size = GetInRawDataSize(), .domain_interface_count = 0, }; } @@ -175,7 +199,7 @@ constexpr RequestLayout GetDomainReplyInLayout() { return RequestLayout{ .copy_handle_count = GetArgumentTypeCount(), .move_handle_count = 0, - .cmif_raw_data_size = GetArgumentRawDataSize(), + .cmif_raw_data_size = GetInRawDataSize(), .domain_interface_count = GetArgumentTypeCount(), }; } @@ -185,7 +209,7 @@ constexpr RequestLayout GetNonDomainReplyOutLayout() { return RequestLayout{ .copy_handle_count = GetArgumentTypeCount(), .move_handle_count = GetArgumentTypeCount() + GetArgumentTypeCount(), - .cmif_raw_data_size = GetArgumentRawDataSize(), + .cmif_raw_data_size = GetOutRawDataSize(), .domain_interface_count = 0, }; } @@ -195,7 +219,7 @@ constexpr RequestLayout GetDomainReplyOutLayout() { return RequestLayout{ .copy_handle_count = GetArgumentTypeCount(), .move_handle_count = GetArgumentTypeCount(), - .cmif_raw_data_size = GetArgumentRawDataSize(), + .cmif_raw_data_size = GetOutRawDataSize(), .domain_interface_count = GetArgumentTypeCount(), }; } @@ -337,13 +361,15 @@ void WriteOutArgument(bool is_domain, CallArguments& args, u8* raw_data, HLERequ using ArgType = std::tuple_element_t; if constexpr (ArgumentTraits::Type == ArgumentType::OutData) { - constexpr size_t ArgAlign = alignof(ArgType); - constexpr size_t ArgSize = sizeof(ArgType); + using RawArgType = decltype(std::get(args).raw); + constexpr size_t ArgAlign = alignof(RawArgType); + constexpr size_t ArgSize = sizeof(RawArgType); static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment"); static_assert(!RawDataFinished, "All output interface arguments must appear after raw data"); static_assert(!std::is_pointer_v, "Output raw data must not be a pointer"); - static_assert(std::is_trivially_copyable_v(args).raw)>, "Output raw data must be trivially copyable"); + static_assert(!std::is_pointer_v, "Output raw data must not be a pointer"); + static_assert(std::is_trivially_copyable_v, "Output raw data must be trivially copyable"); constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); constexpr size_t ArgEnd = ArgOffset + ArgSize; diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp index 679d83d90..5a64576a4 100755 --- a/src/core/hle/service/nvdrv/core/container.cpp +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -83,7 +83,9 @@ SessionId Container::OpenSession(Kernel::KProcess* process) { // Check if this memory block is heap. if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) { - if (svc_mem_info.size > region_size) { + if (region_start + region_size == svc_mem_info.base_address) { + region_size += svc_mem_info.size; + } else if (svc_mem_info.size > region_size) { region_size = svc_mem_info.size; region_start = svc_mem_info.base_address; } diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index a3e73c0a6..25d0c3b94 100755 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -24,6 +24,7 @@ enum class Errno : u32 { CONNRESET = 104, NOTCONN = 107, TIMEDOUT = 110, + CONNREFUSED = 111, INPROGRESS = 115, }; diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index e00f9935a..10912fd0b 100755 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp @@ -25,6 +25,8 @@ Errno Translate(Network::Errno value) { return Errno::MFILE; case Network::Errno::PIPE: return Errno::PIPE; + case Network::Errno::CONNREFUSED: + return Errno::CONNREFUSED; case Network::Errno::NOTCONN: return Errno::NOTCONN; case Network::Errno::TIMEDOUT: diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index e67031281..2dc2f7b99 100755 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp @@ -693,20 +693,23 @@ std::pair Socket::Accept() { sockaddr_in addr; socklen_t addrlen = sizeof(addr); - std::vector host_pollfds{ - WSAPOLLFD{fd, POLLIN, 0}, - WSAPOLLFD{GetInterruptSocket(), POLLIN, 0}, - }; + const bool wait_for_accept = !is_non_blocking; + if (wait_for_accept) { + std::vector host_pollfds{ + WSAPOLLFD{fd, POLLIN, 0}, + WSAPOLLFD{GetInterruptSocket(), POLLIN, 0}, + }; - while (true) { - const int pollres = - WSAPoll(host_pollfds.data(), static_cast(host_pollfds.size()), -1); - if (host_pollfds[1].revents != 0) { - // Interrupt signaled before a client could be accepted, break - return {AcceptResult{}, Errno::AGAIN}; - } - if (pollres > 0) { - break; + while (true) { + const int pollres = + WSAPoll(host_pollfds.data(), static_cast(host_pollfds.size()), -1); + if (host_pollfds[1].revents != 0) { + // Interrupt signaled before a client could be accepted, break + return {AcceptResult{}, Errno::AGAIN}; + } + if (pollres > 0) { + break; + } } } @@ -913,6 +916,7 @@ Errno Socket::SetRcvTimeo(u32 value) { Errno Socket::SetNonBlock(bool enable) { if (EnableNonBlock(fd, enable)) { + is_non_blocking = enable; return Errno::SUCCESS; } return GetAndLogLastError(); diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h index c1599fcb1..e1e7262f7 100755 --- a/src/core/internal_network/sockets.h +++ b/src/core/internal_network/sockets.h @@ -166,6 +166,9 @@ public: bool IsOpened() const override; void HandleProxyPacket(const ProxyPacket& packet) override; + +private: + bool is_non_blocking = false; }; std::pair Poll(std::vector& poll_fds, s32 timeout); diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 56feecc25..ea58428bf 100755 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1431,7 +1431,8 @@ ImageId TextureCache

::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA } } }; - ForEachSparseImageInRegion(gpu_addr, size_bytes, region_check_gpu); + ForEachSparseImageInRegion(channel_state->gpu_memory.GetID(), gpu_addr, size_bytes, + region_check_gpu); bool can_rescale = info.rescaleable; bool any_rescaled = false; @@ -1842,7 +1843,7 @@ void TextureCache

::ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, s if (!storage_id) { return; } - auto& gpu_page_table = gpu_page_table_storage[*storage_id]; + auto& gpu_page_table = gpu_page_table_storage[*storage_id * 2]; ForEachGPUPage(gpu_addr, size, [this, &gpu_page_table, &images, gpu_addr, size, func](u64 page) { const auto it = gpu_page_table.find(page); @@ -1882,41 +1883,48 @@ void TextureCache

::ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, s template template -void TextureCache

::ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func) { +void TextureCache

::ForEachSparseImageInRegion(size_t as_id, GPUVAddr gpu_addr, size_t size, + Func&& func) { using FuncReturn = typename std::invoke_result::type; static constexpr bool BOOL_BREAK = std::is_same_v; boost::container::small_vector images; - ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) { - const auto it = sparse_page_table.find(page); - if (it == sparse_page_table.end()) { - if constexpr (BOOL_BREAK) { - return false; - } else { - return; - } - } - for (const ImageId image_id : it->second) { - Image& image = slot_images[image_id]; - if (True(image.flags & ImageFlagBits::Picked)) { - continue; - } - if (!image.OverlapsGPU(gpu_addr, size)) { - continue; - } - image.flags |= ImageFlagBits::Picked; - images.push_back(image_id); - if constexpr (BOOL_BREAK) { - if (func(image_id, image)) { - return true; - } - } else { - func(image_id, image); - } - } - if constexpr (BOOL_BREAK) { - return false; - } - }); + auto storage_id = getStorageID(as_id); + if (!storage_id) { + return; + } + auto& sparse_page_table = gpu_page_table_storage[*storage_id * 2 + 1]; + ForEachGPUPage(gpu_addr, size, + [this, &sparse_page_table, &images, gpu_addr, size, func](u64 page) { + const auto it = sparse_page_table.find(page); + if (it == sparse_page_table.end()) { + if constexpr (BOOL_BREAK) { + return false; + } else { + return; + } + } + for (const ImageId image_id : it->second) { + Image& image = slot_images[image_id]; + if (True(image.flags & ImageFlagBits::Picked)) { + continue; + } + if (!image.OverlapsGPU(gpu_addr, size)) { + continue; + } + image.flags |= ImageFlagBits::Picked; + images.push_back(image_id); + if constexpr (BOOL_BREAK) { + if (func(image_id, image)) { + return true; + } + } else { + func(image_id, image); + } + } + if constexpr (BOOL_BREAK) { + return false; + } + }); for (const ImageId image_id : images) { slot_images[image_id].flags &= ~ImageFlagBits::Picked; } @@ -1988,8 +1996,9 @@ void TextureCache

::RegisterImage(ImageId image_id) { sparse_maps.push_back(map_id); }); sparse_views.emplace(image_id, std::move(sparse_maps)); - ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, - [this, image_id](u64 page) { sparse_page_table[page].push_back(image_id); }); + ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, image_id](u64 page) { + (*channel_state->sparse_page_table)[page].push_back(image_id); + }); } template @@ -2042,7 +2051,7 @@ void TextureCache

::UnregisterImage(ImageId image_id) { return; } ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) { - clear_page_table(page, sparse_page_table); + clear_page_table(page, (*channel_state->sparse_page_table)); }); auto it = sparse_views.find(image_id); ASSERT(it != sparse_views.end()); @@ -2496,13 +2505,15 @@ void TextureCache

::CreateChannel(struct Tegra::Control::ChannelState& channel const auto it = channel_map.find(channel.bind_id); auto* this_state = &channel_storage[it->second]; const auto& this_as_ref = address_spaces[channel.memory_manager->GetID()]; - this_state->gpu_page_table = &gpu_page_table_storage[this_as_ref.storage_id]; + this_state->gpu_page_table = &gpu_page_table_storage[this_as_ref.storage_id * 2]; + this_state->sparse_page_table = &gpu_page_table_storage[this_as_ref.storage_id * 2 + 1]; } /// Bind a channel for execution. template void TextureCache

::OnGPUASRegister([[maybe_unused]] size_t map_id) { gpu_page_table_storage.emplace_back(); + gpu_page_table_storage.emplace_back(); } } // namespace VideoCommon diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index d76106769..f793aee8c 100755 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -86,6 +86,7 @@ public: std::unordered_map samplers; TextureCacheGPUMap* gpu_page_table; + TextureCacheGPUMap* sparse_page_table; }; template @@ -357,7 +358,7 @@ private: void ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size, Func&& func); template - void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func); + void ForEachSparseImageInRegion(size_t as_id, GPUVAddr gpu_addr, size_t size, Func&& func); /// Iterates over all the images in a region calling func template @@ -431,7 +432,6 @@ private: std::unordered_map framebuffers; std::unordered_map, Common::IdentityHash> page_table; - std::unordered_map, Common::IdentityHash> sparse_page_table; std::unordered_map> sparse_views; DAddr virtual_invalid_space{};