diff --git a/README.md b/README.md index 4d1bf4cd3..291256d6f 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 4083. +This is the source code for early-access 4084. ## Legal Notice diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index f1483331f..b008fbe6b 100755 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -106,6 +106,7 @@ add_library(common STATIC precompiled_headers.h quaternion.h range_map.h + range_mutex.h reader_writer_queue.h ring_buffer.h ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp diff --git a/src/common/range_mutex.h b/src/common/range_mutex.h new file mode 100755 index 000000000..d6c949811 --- /dev/null +++ b/src/common/range_mutex.h @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/intrusive_list.h" + +namespace Common { + +class ScopedRangeLock; + +class RangeMutex { +public: + explicit RangeMutex() = default; + ~RangeMutex() = default; + +private: + friend class ScopedRangeLock; + + void Lock(ScopedRangeLock& l); + void Unlock(ScopedRangeLock& l); + bool HasIntersectionLocked(ScopedRangeLock& l); + +private: + std::mutex m_mutex; + std::condition_variable m_cv; + + using LockList = Common::IntrusiveListBaseTraits::ListType; + LockList m_list; +}; + +class ScopedRangeLock : public Common::IntrusiveListBaseNode { +public: + explicit ScopedRangeLock(RangeMutex& mutex, u64 address, u64 size) + : m_mutex(mutex), m_address(address), m_size(size) { + if (m_size > 0) { + m_mutex.Lock(*this); + } + } + ~ScopedRangeLock() { + if (m_size > 0) { + m_mutex.Unlock(*this); + } + } + + u64 GetAddress() const { + return m_address; + } + + u64 GetSize() const { + return m_size; + } + +private: + RangeMutex& m_mutex; + const u64 m_address{}; + const u64 m_size{}; +}; + +inline void RangeMutex::Lock(ScopedRangeLock& l) { + std::unique_lock lk{m_mutex}; + m_cv.wait(lk, [&] { return !HasIntersectionLocked(l); }); + m_list.push_back(l); +} + +inline void RangeMutex::Unlock(ScopedRangeLock& l) { + { + std::scoped_lock lk{m_mutex}; + m_list.erase(m_list.iterator_to(l)); + } + m_cv.notify_all(); +} + +inline bool RangeMutex::HasIntersectionLocked(ScopedRangeLock& l) { + const auto cur_begin = l.GetAddress(); + const auto cur_last = l.GetAddress() + l.GetSize() - 1; + + for (const auto& other : m_list) { + const auto other_begin = other.GetAddress(); + const auto other_last = other.GetAddress() + other.GetSize() - 1; + + if (cur_begin <= other_last && other_begin <= cur_last) { + return true; + } + } + + return false; +} + +} // namespace Common diff --git a/src/common/settings.h b/src/common/settings.h index db8606f93..a3e34528b 100755 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -420,8 +420,15 @@ struct Values { SwitchableSetting custom_rtc{ linkage, 0, "custom_rtc", Category::System, Specialization::Time, false, true, &custom_rtc_enabled}; - SwitchableSetting custom_rtc_offset{ - linkage, 0, "custom_rtc_offset", Category::System, Specialization::Countable, true, true}; + SwitchableSetting custom_rtc_offset{linkage, + 0, + std::numeric_limits::min(), + std::numeric_limits::max(), + "custom_rtc_offset", + Category::System, + Specialization::Countable, + true, + true}; SwitchableSetting rng_seed_enabled{ linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true}; SwitchableSetting rng_seed{ diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h index ffeed46cc..63823602c 100755 --- a/src/core/device_memory_manager.h +++ b/src/core/device_memory_manager.h @@ -10,6 +10,7 @@ #include #include "common/common_types.h" +#include "common/range_mutex.h" #include "common/scratch_buffer.h" #include "common/virtual_buffer.h" @@ -204,7 +205,7 @@ private: (1ULL << (device_virtual_bits - page_bits)) / subentries; using CachedPages = std::array; std::unique_ptr cached_pages; - std::mutex counter_guard; + Common::RangeMutex counter_guard; std::mutex mapping_guard; }; diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index 8ce122872..0a59000aa 100755 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -31,9 +31,8 @@ public: buffer.resize(0); size_t index = 0; const auto add_value = [&](u32 value) { - buffer[index] = value; - index++; - buffer.resize(index); + buffer.resize(index + 1); + buffer[index++] = value; }; u32 iter_entry = start_entry; @@ -509,12 +508,7 @@ void DeviceMemoryManager::UnregisterProcess(Asid asid) { template void DeviceMemoryManager::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) { - std::unique_lock lk(counter_guard, std::defer_lock); - const auto Lock = [&] { - if (!lk) { - lk.lock(); - } - }; + Common::ScopedRangeLock lk(counter_guard, addr, size); u64 uncache_begin = 0; u64 cache_begin = 0; u64 uncache_bytes = 0; @@ -549,7 +543,6 @@ void DeviceMemoryManager::UpdatePagesCachedCount(DAddr addr, size_t size } uncache_bytes += Memory::YUZU_PAGESIZE; } else if (uncache_bytes > 0) { - Lock(); MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes, false); uncache_bytes = 0; @@ -560,7 +553,6 @@ void DeviceMemoryManager::UpdatePagesCachedCount(DAddr addr, size_t size } cache_bytes += Memory::YUZU_PAGESIZE; } else if (cache_bytes > 0) { - Lock(); MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes, true); cache_bytes = 0; @@ -568,12 +560,10 @@ void DeviceMemoryManager::UpdatePagesCachedCount(DAddr addr, size_t size vpage++; } if (uncache_bytes > 0) { - Lock(); MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes, false); } if (cache_bytes > 0) { - Lock(); MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes, true); } diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp index 09caaface..2d62a92a5 100755 --- a/src/core/file_sys/system_archive/system_archive.cpp +++ b/src/core/file_sys/system_archive/system_archive.cpp @@ -67,25 +67,29 @@ constexpr std::array SYSTEM_ARCHI }}; VirtualFile SynthesizeSystemArchive(const u64 title_id) { - if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) + if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) { return nullptr; + } const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID]; LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id); - if (desc.supplier == nullptr) + if (desc.supplier == nullptr) { return nullptr; + } const auto dir = desc.supplier(); - if (dir == nullptr) + if (dir == nullptr) { return nullptr; + } const auto romfs = CreateRomFS(dir); - if (romfs == nullptr) + if (romfs == nullptr) { return nullptr; + } LOG_INFO(Service_FS, " - System archive generation successful!"); return romfs; diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp index afaea4135..6423e5089 100755 --- a/src/core/hle/service/glue/time/manager.cpp +++ b/src/core/hle/service/glue/time/manager.cpp @@ -89,7 +89,8 @@ Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationN std::min(configured_name.name.size(), configured_zone.size())); } - ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone!"); + ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!", + configured_name.name.data()); return configured_name; } diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp index 96530dce5..67969aa3f 100755 --- a/src/core/hle/service/glue/time/time_zone_binary.cpp +++ b/src/core/hle/service/glue/time/time_zone_binary.cpp @@ -61,6 +61,16 @@ Result MountTimeZoneBinary(Core::System& system) { g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS()); } + if (g_time_zone_binary_romfs) { + // Validate that the romfs is readable, using invalid firmware keys can cause this to get + // set but the files to be garbage. In that case, we want to hit the next path and + // synthesise them instead. + Service::PSC::Time::LocationName name{"Etc/GMT"}; + if (!IsTimeZoneBinaryValid(name)) { + ResetTimeZoneBinary(); + } + } + if (!g_time_zone_binary_romfs) { g_time_zone_binary_romfs = FileSys::ExtractRomFS( FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId)); @@ -102,6 +112,7 @@ bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) { auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)}; if (!vfs_file) { + LOG_INFO(Service_Time, "Could not find timezone file {}", path); return false; } return vfs_file->GetSize() != 0; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 1db44d504..4b046a1f8 100755 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1093,6 +1093,20 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { [&] { rasterizer = true; }); if (rasterizer) { impl->InvalidateGPUMemory(ptr, size); + + const auto type = impl->current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type(); + if (type == Common::PageType::RasterizerCachedMemory) { + // Check if device mapped. If not, this bugged and we can unmark. + DAddr addr{}; + Common::ScratchBuffer buffer; + impl->gpu_device_memory->ApplyOpOnPointer(ptr, buffer, + [&](DAddr address) { addr = address; }); + + if (addr == 0) { + LOG_ERROR(HW_Memory, "Fixing unmapped cached region {:#x}", GetInteger(vaddr)); + impl->RasterizerMarkRegionCached(GetInteger(vaddr), size, false); + } + } } #ifdef __linux__ diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 74fd10286..61b1e85bf 100755 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1546,7 +1546,10 @@ void BufferCache

::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer, std::span upload_span; const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset; if (IsRangeGranular(device_addr, copy.size)) { - upload_span = std::span(device_memory.GetPointer(device_addr), copy.size); + auto* const ptr = device_memory.GetPointer(device_addr); + if (ptr != nullptr) { + upload_span = std::span(ptr, copy.size); + } } else { if (immediate_buffer.empty()) { immediate_buffer = ImmediateBuffer(largest_copy); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 18bbfc348..1b3a6916f 100755 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -243,10 +243,12 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf const u64 size_in_bytes{Tegra::Texture::CalculateSize( true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; const u8* const host_ptr{device_memory.GetPointer(framebuffer_addr)}; - const std::span input_data(host_ptr, size_in_bytes); - Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, - framebuffer.width, framebuffer.height, 1, block_height_log2, - 0); + if (host_ptr != nullptr) { + const std::span input_data(host_ptr, size_in_bytes); + Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, + framebuffer.width, framebuffer.height, 1, + block_height_log2, 0); + } glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(framebuffer.stride)); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 3c0299a85..f632005f5 100755 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -230,9 +230,11 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; - Tegra::Texture::UnswizzleTexture( - mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), - bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); + if (host_ptr != nullptr) { + Tegra::Texture::UnswizzleTexture( + mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), + bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); + } const VkBufferImageCopy copy{ .bufferOffset = image_offset, diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 025d250ba..56ac24b67 100755 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -1064,8 +1064,6 @@ public: } }); } - auto* ptr = device_memory.GetPointer(new_query->dependant_address); - ASSERT(ptr != nullptr); new_query->dependant_manage = must_manage_dependance; pending_flush_queries.push_back(index); @@ -1104,9 +1102,11 @@ public: tfb_streamer.Free(query->dependant_index); } else { u8* pointer = device_memory.GetPointer(query->dependant_address); - u32 result; - std::memcpy(&result, pointer, sizeof(u32)); - num_vertices = static_cast(result) / query->stride; + if (pointer != nullptr) { + u32 result; + std::memcpy(&result, pointer, sizeof(u32)); + num_vertices = static_cast(result) / query->stride; + } } query->value = [&]() -> u64 { switch (query->topology) { @@ -1360,7 +1360,9 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku const auto check_value = [&](DAddr address) { u8* ptr = impl->device_memory.GetPointer(address); u64 value{}; - std::memcpy(&value, ptr, sizeof(value)); + if (ptr != nullptr) { + std::memcpy(&value, ptr, sizeof(value)); + } return value == 0; }; std::array objects{&object_1, &object_2};