diff --git a/CMakeLists.txt b/CMakeLists.txt index 53a2281ec..7962488c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,7 +227,7 @@ find_package(SDL3 3.1.2 CONFIG) find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) -find_package(VulkanHeaders 1.4.309 CONFIG) +find_package(VulkanHeaders 1.4.314 CONFIG) find_package(VulkanMemoryAllocator 3.1.0 CONFIG) find_package(xbyak 7.07 CONFIG) find_package(xxHash 0.8.2 MODULE) @@ -622,6 +622,8 @@ set(DEV_TOOLS src/core/devtools/layer.cpp src/core/devtools/widget/imgui_memory_editor.h src/core/devtools/widget/memory_map.cpp src/core/devtools/widget/memory_map.h + src/core/devtools/widget/module_list.cpp + src/core/devtools/widget/module_list.h src/core/devtools/widget/reg_popup.cpp src/core/devtools/widget/reg_popup.h src/core/devtools/widget/reg_view.cpp diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK index 87a8e8b13..3a0b07a24 160000 --- a/externals/MoltenVK/MoltenVK +++ b/externals/MoltenVK/MoltenVK @@ -1 +1 @@ -Subproject commit 87a8e8b13d4ad8835367fea1ebad1896d0460946 +Subproject commit 3a0b07a24a4a681ffe70b461b1f4333b2729e2ef diff --git a/externals/MoltenVK/SPIRV-Cross b/externals/MoltenVK/SPIRV-Cross index 791877574..969e75f7c 160000 --- a/externals/MoltenVK/SPIRV-Cross +++ b/externals/MoltenVK/SPIRV-Cross @@ -1 +1 @@ -Subproject commit 7918775748c5e2f5c40d9918ce68825035b5a1e1 +Subproject commit 969e75f7cc0718774231d029f9d52fa87d4ae1b2 diff --git a/externals/vulkan-headers b/externals/vulkan-headers index 5ceb9ed48..9c77de5c3 160000 --- a/externals/vulkan-headers +++ b/externals/vulkan-headers @@ -1 +1 @@ -Subproject commit 5ceb9ed481e58e705d0d9b5326537daedd06b97d +Subproject commit 9c77de5c3dd216f28e407eec65ed9c0a296c1f74 diff --git a/src/common/elf_info.h b/src/common/elf_info.h index 062cee012..02b845cb5 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -71,6 +71,7 @@ class ElfInfo { PSFAttributes psf_attributes{}; std::filesystem::path splash_path{}; + std::filesystem::path game_folder{}; public: static constexpr u32 FW_15 = 0x1500000; @@ -123,6 +124,10 @@ public: [[nodiscard]] const std::filesystem::path& GetSplashPath() const { return splash_path; } + + [[nodiscard]] const std::filesystem::path& GetGameFolder() const { + return game_folder; + } }; } // namespace Common diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index a93178de5..5380d3be9 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -17,6 +17,7 @@ #include "widget/frame_dump.h" #include "widget/frame_graph.h" #include "widget/memory_map.h" +#include "widget/module_list.h" #include "widget/shader_list.h" extern std::unique_ptr presenter; @@ -40,6 +41,7 @@ static bool just_opened_options = false; static Widget::MemoryMapViewer memory_map; static Widget::ShaderList shader_list; +static Widget::ModuleList module_list; // clang-format off static std::string help_text = @@ -108,6 +110,9 @@ void L::DrawMenuBar() { if (MenuItem("Memory map")) { memory_map.open = true; } + if (MenuItem("Module list")) { + module_list.open = true; + } ImGui::EndMenu(); } @@ -256,6 +261,9 @@ void L::DrawAdvanced() { if (shader_list.open) { shader_list.Draw(); } + if (module_list.open) { + module_list.Draw(); + } } void L::DrawSimple() { diff --git a/src/core/devtools/widget/module_list.cpp b/src/core/devtools/widget/module_list.cpp new file mode 100644 index 000000000..73afe3462 --- /dev/null +++ b/src/core/devtools/widget/module_list.cpp @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "module_list.h" + +#include + +#include "common.h" +#include "core/debug_state.h" +#include "imgui/imgui_std.h" + +using namespace ImGui; + +namespace Core::Devtools::Widget { +void ModuleList::Draw() { + SetNextWindowSize({550.0f, 600.0f}, ImGuiCond_FirstUseEver); + if (!Begin("Module List", &open)) { + End(); + return; + } + + if (BeginTable("ModuleTable", 3, + ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | + ImGuiTableFlags_RowBg)) { + TableSetupColumn("Modulname", ImGuiTableColumnFlags_WidthStretch); + TableHeadersRow(); + + std::scoped_lock lock(modules_mutex); + for (const auto& module : modules) { + TableNextRow(); + + TableSetColumnIndex(0); + TextUnformatted(module.name.c_str()); + + TableSetColumnIndex(1); + if (module.is_sys_module) { + TextColored({0.2f, 0.6f, 0.8f, 1.0f}, "System Module"); + } else { + TextColored({0.8f, 0.4f, 0.2f, 1.0f}, "Game Module"); + } + + TableSetColumnIndex(2); + if (module.is_lle) { + TextColored({0.4f, 0.7f, 0.4f, 1.0f}, "LLE"); + } else { + TextColored({0.7f, 0.4f, 0.5f, 1.0f}, "HLE"); + } + } + EndTable(); + } + + End(); +} + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/module_list.h b/src/core/devtools/widget/module_list.h new file mode 100644 index 000000000..4c961919e --- /dev/null +++ b/src/core/devtools/widget/module_list.h @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include "common/elf_info.h" +#include "common/path_util.h" + +namespace Core::Devtools::Widget { + +class ModuleList { +public: + ModuleList() = default; + ~ModuleList() = default; + + void Draw(); + bool open = false; + + static bool IsSystemModule(const std::filesystem::path& path) { + const auto sys_modules_path = Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir); + + const auto abs_path = std::filesystem::absolute(path).lexically_normal(); + const auto abs_sys_path = std::filesystem::absolute(sys_modules_path).lexically_normal(); + + const auto path_str = abs_path.string(); + const auto sys_path_str = abs_sys_path.string(); + + return path_str.starts_with(sys_path_str); + } + + static bool IsSystemModule(const std::string& name) { + const auto game_modules_path = Common::ElfInfo::Instance().GetGameFolder() / "sce_module"; + const auto prx_path = game_modules_path / name; + + if (!std::filesystem::exists(prx_path)) { + return true; + } + return false; + } + + static void AddModule(const std::string& name, std::filesystem::path path) { + if (name == "eboot.bin") { + return; + } + std::scoped_lock lock(modules_mutex); + modules.push_back({name, IsSystemModule(path), true}); + } + + static void AddModule(std::string name) { + name = name + ".prx"; + std::scoped_lock lock(modules_mutex); + + bool is_sys_module = IsSystemModule(name); + bool is_lle = false; + auto it = std::find_if(modules.begin(), modules.end(), + [&name, is_sys_module, is_lle](const ModuleInfo& entry) { + return entry.name == name && !entry.is_lle; + }); + + if (it == modules.end()) { + modules.push_back({name, is_sys_module, is_lle}); + } + } + +private: + struct ModuleInfo { + std::string name; + bool is_sys_module; + bool is_lle; + }; + + static inline std::mutex modules_mutex; + + static inline std::vector modules; +}; + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/linker.cpp b/src/core/linker.cpp index eced87968..3e6d8c22e 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -12,6 +12,7 @@ #include "common/thread.h" #include "core/aerolib/aerolib.h" #include "core/aerolib/stubs.h" +#include "core/devtools/widget/module_list.h" #include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/threads.h" #include "core/linker.h" @@ -147,6 +148,9 @@ s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) { num_static_modules += !is_dynamic; m_modules.emplace_back(std::move(module)); + + Core::Devtools::Widget::ModuleList::AddModule(elf_name.filename().string(), elf_name); + return m_modules.size() - 1; } @@ -325,6 +329,9 @@ bool Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul } if (record) { *return_info = *record; + + Core::Devtools::Widget::ModuleList::AddModule(sr.library); + return true; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 9a0429d5d..2ad8446ab 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -25,6 +25,7 @@ #include "common/polyfill_thread.h" #include "common/scm_rev.h" #include "common/singleton.h" +#include "core/devtools/widget/module_list.h" #include "core/file_format/psf.h" #include "core/file_format/trp.h" #include "core/file_sys/fs.h" @@ -188,6 +189,8 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector", id, title, app_version); std::string window_title = ""; std::string remote_url(Common::g_scm_remote_url); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp index 347c4cb0a..01c51e399 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp @@ -154,7 +154,7 @@ Id EmitFPRecip32(EmitContext& ctx, Id value) { } Id EmitFPRecip64(EmitContext& ctx, Id value) { - return ctx.OpFDiv(ctx.F64[1], ctx.Constant(ctx.F64[1], 1.0f), value); + return ctx.OpFDiv(ctx.F64[1], ctx.Constant(ctx.F64[1], f64{1.0}), value); } Id EmitFPRecipSqrt32(EmitContext& ctx, Id value) { diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 706e94c2b..4db7648c6 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -752,6 +752,10 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanbuffer_select.Value()); break; } + case PM4ItOpcode::GetLodStats: { + LOG_WARNING(Render_Vulkan, "Unimplemented IT_GET_LOD_STATS"); + break; + } default: UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}", static_cast(opcode), count); diff --git a/src/video_core/amdgpu/pm4_opcodes.h b/src/video_core/amdgpu/pm4_opcodes.h index ce388d1ba..4a5f2be4e 100644 --- a/src/video_core/amdgpu/pm4_opcodes.h +++ b/src/video_core/amdgpu/pm4_opcodes.h @@ -71,6 +71,7 @@ enum class PM4ItOpcode : u32 { IncrementDeCounter = 0x85, WaitOnCeCounter = 0x86, WaitOnDeCounterDiff = 0x88, + GetLodStats = 0x8E, DrawIndexIndirectCountMulti = 0x9d, }; diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 45863d8e8..4717a5ff8 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -668,7 +668,7 @@ void BufferCache::ProcessFaultBuffer() { const VAddr fault_end = fault + CACHING_PAGESIZE; // This can be adjusted fault_ranges += boost::icl::interval_set::interval_type::right_open(fault, fault_end); - LOG_INFO(Render_Vulkan, "Accessed non-GPU mapped memory at {:#x}", fault); + LOG_INFO(Render_Vulkan, "Accessed non-GPU cached memory at {:#x}", fault); } for (const auto& range : fault_ranges) { const VAddr start = range.lower(); diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index 36145d0c5..39c03e7da 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -213,6 +213,12 @@ struct PageManager::Impl { // Iterate requested pages const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); + const u64 aligned_addr = page << PAGE_BITS; + const u64 aligned_end = page_end << PAGE_BITS; + ASSERT_MSG(rasterizer->IsMapped(aligned_addr, aligned_end - aligned_addr), + "Attempted to track non-GPU memory at address {:#x}, size {:#x}.", + aligned_addr, aligned_end - aligned_addr); + for (; page != page_end; ++page) { PageState& state = cached_pages[page]; diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 409085511..ddb4ea799 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -222,14 +222,23 @@ std::tuple TextureCache::ResolveOverlap(const ImageInfo& imag -1, -1}; } - ImageId new_image_id{}; - if (image_info.type == tex_cache_image.info.type) { - ASSERT(image_info.resources > tex_cache_image.info.resources); - new_image_id = ExpandImage(image_info, cache_image_id); - } else { - UNREACHABLE(); + if (image_info.type == tex_cache_image.info.type && + image_info.resources > tex_cache_image.info.resources) { + // Size and resources are greater, expand the image. + return {ExpandImage(image_info, cache_image_id), -1, -1}; } - return {new_image_id, -1, -1}; + + if (image_info.tiling_mode != tex_cache_image.info.tiling_mode) { + // Size is greater but resources are not, because the tiling mode is different. + // Likely this memory address is being reused for a different image with a different + // tiling mode. + if (safe_to_delete) { + FreeImage(cache_image_id); + } + return {merged_image_id, -1, -1}; + } + + UNREACHABLE_MSG("Encountered unresolvable image overlap with equal memory address."); } // Right overlap, the image requested is a possible subresource of the image from cache. @@ -538,10 +547,16 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule image.mip_hashes[m] = hash; } + auto mip_pitch = static_cast(mip.pitch); + auto mip_height = static_cast(mip.height); + + auto image_extent_width = mip_pitch ? std::min(mip_pitch, width) : width; + auto image_extent_height = mip_height ? std::min(mip_height, height) : height; + image_copy.push_back({ .bufferOffset = mip.offset, - .bufferRowLength = static_cast(mip.pitch), - .bufferImageHeight = static_cast(mip.height), + .bufferRowLength = mip_pitch, + .bufferImageHeight = mip_height, .imageSubresource{ .aspectMask = image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil, .mipLevel = m, @@ -549,7 +564,7 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule .layerCount = num_layers, }, .imageOffset = {0, 0, 0}, - .imageExtent = {width, height, depth}, + .imageExtent = {image_extent_width, image_extent_height, depth}, }); }