mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-26 03:55:37 +00:00
Merge branch 'shadps4-emu:main' into equeue-fixes
This commit is contained in:
commit
460fc4e678
@ -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
|
||||
|
2
externals/MoltenVK/MoltenVK
vendored
2
externals/MoltenVK/MoltenVK
vendored
@ -1 +1 @@
|
||||
Subproject commit 87a8e8b13d4ad8835367fea1ebad1896d0460946
|
||||
Subproject commit 3a0b07a24a4a681ffe70b461b1f4333b2729e2ef
|
2
externals/MoltenVK/SPIRV-Cross
vendored
2
externals/MoltenVK/SPIRV-Cross
vendored
@ -1 +1 @@
|
||||
Subproject commit 7918775748c5e2f5c40d9918ce68825035b5a1e1
|
||||
Subproject commit 969e75f7cc0718774231d029f9d52fa87d4ae1b2
|
2
externals/vulkan-headers
vendored
2
externals/vulkan-headers
vendored
@ -1 +1 @@
|
||||
Subproject commit 5ceb9ed481e58e705d0d9b5326537daedd06b97d
|
||||
Subproject commit 9c77de5c3dd216f28e407eec65ed9c0a296c1f74
|
@ -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
|
||||
|
@ -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<Vulkan::Presenter> 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() {
|
||||
|
55
src/core/devtools/widget/module_list.cpp
Normal file
55
src/core/devtools/widget/module_list.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "module_list.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#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
|
82
src/core/devtools/widget/module_list.h
Normal file
82
src/core/devtools/widget/module_list.h
Normal file
@ -0,0 +1,82 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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<ModuleInfo> modules;
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
@ -98,6 +98,11 @@ bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
|
||||
}
|
||||
|
||||
int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) {
|
||||
if (HasSmallTimer()) {
|
||||
// If a small timer is set, just wait for it to expire.
|
||||
return WaitForSmallTimer(ev, num, micros);
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
const auto predicate = [&] {
|
||||
@ -185,7 +190,8 @@ int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) {
|
||||
ASSERT(num == 1);
|
||||
|
||||
auto curr_clock = std::chrono::steady_clock::now();
|
||||
const auto wait_end_us = curr_clock + std::chrono::microseconds{micros};
|
||||
const auto wait_end_us = (micros == 0) ? std::chrono::steady_clock::time_point::max()
|
||||
: curr_clock + std::chrono::microseconds{micros};
|
||||
|
||||
do {
|
||||
curr_clock = std::chrono::steady_clock::now();
|
||||
@ -264,24 +270,15 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
if (eq->HasSmallTimer()) {
|
||||
ASSERT(timo && *timo);
|
||||
*out = eq->WaitForSmallTimer(ev, num, *timo);
|
||||
if (timo == nullptr) {
|
||||
// When the timeout is nullptr, we wait indefinitely
|
||||
*out = eq->WaitForEvents(ev, num, 0);
|
||||
} else if (*timo == 0) {
|
||||
// Only events that have already arrived at the time of this function call can be received
|
||||
*out = eq->GetTriggeredEvents(ev, num);
|
||||
} else {
|
||||
if (timo == nullptr) { // wait until an event arrives without timing out
|
||||
*out = eq->WaitForEvents(ev, num, 0);
|
||||
}
|
||||
|
||||
if (timo != nullptr) {
|
||||
// Only events that have already arrived at the time of this function call can be
|
||||
// received
|
||||
if (*timo == 0) {
|
||||
*out = eq->GetTriggeredEvents(ev, num);
|
||||
} else {
|
||||
// Wait until an event arrives with timing out
|
||||
*out = eq->WaitForEvents(ev, num, *timo);
|
||||
}
|
||||
}
|
||||
// Wait for up to the specified timeout value
|
||||
*out = eq->WaitForEvents(ev, num, *timo);
|
||||
}
|
||||
|
||||
if (*out == 0) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<std::str
|
||||
game_info.splash_path = pic1_path;
|
||||
}
|
||||
|
||||
game_info.game_folder = game_folder;
|
||||
|
||||
std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version);
|
||||
std::string window_title = "";
|
||||
std::string remote_url(Common::g_scm_remote_url);
|
||||
|
@ -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) {
|
||||
|
@ -752,6 +752,10 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
strmout->buffer_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<u32>(opcode), count);
|
||||
|
@ -71,6 +71,7 @@ enum class PM4ItOpcode : u32 {
|
||||
IncrementDeCounter = 0x85,
|
||||
WaitOnCeCounter = 0x86,
|
||||
WaitOnDeCounterDiff = 0x88,
|
||||
GetLodStats = 0x8E,
|
||||
DrawIndexIndirectCountMulti = 0x9d,
|
||||
};
|
||||
|
||||
|
@ -668,7 +668,7 @@ void BufferCache::ProcessFaultBuffer() {
|
||||
const VAddr fault_end = fault + CACHING_PAGESIZE; // This can be adjusted
|
||||
fault_ranges +=
|
||||
boost::icl::interval_set<VAddr>::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();
|
||||
|
@ -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];
|
||||
|
||||
|
@ -222,14 +222,23 @@ std::tuple<ImageId, int, int> 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<u32>(mip.pitch);
|
||||
auto mip_height = static_cast<u32>(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<u32>(mip.pitch),
|
||||
.bufferImageHeight = static_cast<u32>(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},
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user