IOS/ES: Emulate FS timings for content wrapper IPC commands

Filesystem accesses aren't magically faster when they are done by ES,
so this commit changes our content wrapper IPC commands to take FS
access times and read operations into account.

This should make content read timings a lot more accurate and closer
to console. Note that the accuracy of the timings are limited to the
accuracy of the emulated FS timings, and currently performance
differences between IOS9-IOS28 and newer IOS versions are not emulated.

Part 1 of fixing https://bugs.dolphin-emu.org/issues/11346
(part 2 will involve emulating those differences)
This commit is contained in:
Léo Lam 2021-02-14 00:58:27 +01:00
parent 5eca82a6f2
commit f750208aa3
No known key found for this signature in database
GPG Key ID: 0DF30F9081000741
3 changed files with 71 additions and 63 deletions

View File

@ -105,10 +105,10 @@ public:
std::vector<std::array<u8, 20>> GetSharedContents() const;
// Title contents
s32 OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid);
ReturnCode CloseContent(u32 cfd, u32 uid);
s32 ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid);
s32 SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid);
s32 OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid, Ticks ticks = {});
s32 CloseContent(u32 cfd, u32 uid, Ticks ticks = {});
s32 ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid, Ticks ticks = {});
s32 SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid, Ticks ticks = {});
// Title management
enum class TicketImportType
@ -371,7 +371,7 @@ private:
struct OpenedContent
{
bool m_opened = false;
FS::Fd m_fd;
u64 m_fd;
u64 m_title_id = 0;
ES::Content m_content;
u32 m_uid = 0;

View File

@ -4,18 +4,17 @@
#include "Core/IOS/ES/ES.h"
#include <utility>
#include <vector>
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Core/HW/Memmap.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/IOS/FS/FileSystemProxy.h"
#include "Core/IOS/Uids.h"
namespace IOS::HLE
{
s32 ESDevice::OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid)
s32 ESDevice::OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid, Ticks ticks)
{
const u64 title_id = tmd.GetTitleId();
@ -29,13 +28,13 @@ s32 ESDevice::OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid)
if (entry.m_opened)
continue;
auto file = m_ios.GetFS()->OpenFile(PID_KERNEL, PID_KERNEL, GetContentPath(title_id, content),
FS::Mode::Read);
if (!file)
return FS::ConvertResult(file.Error());
const std::string path = GetContentPath(title_id, content, ticks);
s64 fd = m_ios.GetFSDevice()->Open(PID_KERNEL, PID_KERNEL, path, FS::Mode::Read, {}, ticks);
if (fd < 0)
return fd;
entry.m_opened = true;
entry.m_fd = file->Release();
entry.m_fd = fd;
entry.m_content = content;
entry.m_title_id = title_id;
entry.m_uid = uid;
@ -50,43 +49,48 @@ s32 ESDevice::OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid)
IPCReply ESDevice::OpenContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(3, 0) || request.in_vectors[0].size != sizeof(u64) ||
request.in_vectors[1].size != sizeof(ES::TicketView) ||
request.in_vectors[2].size != sizeof(u32))
{
return IPCReply(ES_EINVAL);
}
return MakeIPCReply(IPC_OVERHEAD_TICKS, [&](Ticks ticks) -> s32 {
if (!request.HasNumberOfValidVectors(3, 0) || request.in_vectors[0].size != sizeof(u64) ||
request.in_vectors[1].size != sizeof(ES::TicketView) ||
request.in_vectors[2].size != sizeof(u32))
{
return ES_EINVAL;
}
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
const u32 content_index = Memory::Read_U32(request.in_vectors[2].address);
// TODO: check the ticket view, check permissions.
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
const u32 content_index = Memory::Read_U32(request.in_vectors[2].address);
// TODO: check the ticket view, check permissions.
const auto tmd = FindInstalledTMD(title_id);
if (!tmd.IsValid())
return IPCReply(FS_ENOENT);
const auto tmd = FindInstalledTMD(title_id, ticks);
if (!tmd.IsValid())
return FS_ENOENT;
return IPCReply(OpenContent(tmd, content_index, uid));
return OpenContent(tmd, content_index, uid, ticks);
});
}
IPCReply ESDevice::OpenActiveTitleContent(u32 caller_uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
return IPCReply(ES_EINVAL);
return MakeIPCReply(IPC_OVERHEAD_TICKS, [&](Ticks ticks) -> s32 {
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
return ES_EINVAL;
const u32 content_index = Memory::Read_U32(request.in_vectors[0].address);
const u32 content_index = Memory::Read_U32(request.in_vectors[0].address);
if (!m_title_context.active)
return IPCReply(ES_EINVAL);
if (!m_title_context.active)
return ES_EINVAL;
ES::UIDSys uid_map{m_ios.GetFSDevice()};
const u32 uid = uid_map.GetOrInsertUIDForTitle(m_title_context.tmd.GetTitleId());
if (caller_uid != 0 && caller_uid != uid)
return IPCReply(ES_EACCES);
ES::UIDSys uid_map{m_ios.GetFSDevice()};
const u32 uid = uid_map.GetOrInsertUIDForTitle(m_title_context.tmd.GetTitleId());
ticks.Add(uid_map.GetTicks());
if (caller_uid != 0 && caller_uid != uid)
return ES_EACCES;
return IPCReply(OpenContent(m_title_context.tmd, content_index, caller_uid));
return OpenContent(m_title_context.tmd, content_index, caller_uid, ticks);
});
}
s32 ESDevice::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid)
s32 ESDevice::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid, Ticks ticks)
{
if (cfd >= m_content_table.size())
return ES_EINVAL;
@ -97,25 +101,26 @@ s32 ESDevice::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid)
if (!entry.m_opened)
return IPC_EINVAL;
const auto result = m_ios.GetFS()->ReadBytesFromFile(entry.m_fd, buffer, size);
return result.Succeeded() ? *result : FS::ConvertResult(result.Error());
return m_ios.GetFSDevice()->Read(entry.m_fd, buffer, size, {}, ticks);
}
IPCReply ESDevice::ReadContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u32))
return IPCReply(ES_EINVAL);
return MakeIPCReply(IPC_OVERHEAD_TICKS, [&](Ticks ticks) -> s32 {
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u32))
return ES_EINVAL;
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
const u32 size = request.io_vectors[0].size;
const u32 addr = request.io_vectors[0].address;
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
const u32 size = request.io_vectors[0].size;
const u32 addr = request.io_vectors[0].address;
INFO_LOG_FMT(IOS_ES, "ReadContent(uid={:#x}, cfd={}, size={}, addr={:08x})", uid, cfd, size,
addr);
return IPCReply(ReadContent(cfd, Memory::GetPointer(addr), size, uid));
INFO_LOG_FMT(IOS_ES, "ReadContent(uid={:#x}, cfd={}, size={}, addr={:08x})", uid, cfd, size,
addr);
return ReadContent(cfd, Memory::GetPointer(addr), size, uid, ticks);
});
}
ReturnCode ESDevice::CloseContent(u32 cfd, u32 uid)
s32 ESDevice::CloseContent(u32 cfd, u32 uid, Ticks ticks)
{
if (cfd >= m_content_table.size())
return ES_EINVAL;
@ -126,7 +131,7 @@ ReturnCode ESDevice::CloseContent(u32 cfd, u32 uid)
if (!entry.m_opened)
return IPC_EINVAL;
m_ios.GetFS()->Close(entry.m_fd);
m_ios.GetFSDevice()->Close(entry.m_fd, ticks);
entry = {};
INFO_LOG_FMT(IOS_ES, "CloseContent: CFD {}", cfd);
return IPC_SUCCESS;
@ -134,14 +139,16 @@ ReturnCode ESDevice::CloseContent(u32 cfd, u32 uid)
IPCReply ESDevice::CloseContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
return IPCReply(ES_EINVAL);
return MakeIPCReply(IPC_OVERHEAD_TICKS, [&](Ticks ticks) -> s32 {
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
return ES_EINVAL;
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
return IPCReply(CloseContent(cfd, uid));
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
return CloseContent(cfd, uid, ticks);
});
}
s32 ESDevice::SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid)
s32 ESDevice::SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid, Ticks ticks)
{
if (cfd >= m_content_table.size())
return ES_EINVAL;
@ -152,19 +159,20 @@ s32 ESDevice::SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid)
if (!entry.m_opened)
return IPC_EINVAL;
const auto result = m_ios.GetFS()->SeekFile(entry.m_fd, offset, static_cast<FS::SeekMode>(mode));
return result.Succeeded() ? *result : FS::ConvertResult(result.Error());
return m_ios.GetFSDevice()->Seek(entry.m_fd, offset, static_cast<FS::SeekMode>(mode), ticks);
}
IPCReply ESDevice::SeekContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(3, 0))
return IPCReply(ES_EINVAL);
return MakeIPCReply(IPC_OVERHEAD_TICKS, [&](Ticks ticks) -> s32 {
if (!request.HasNumberOfValidVectors(3, 0))
return ES_EINVAL;
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
const u32 offset = Memory::Read_U32(request.in_vectors[1].address);
const SeekMode mode = static_cast<SeekMode>(Memory::Read_U32(request.in_vectors[2].address));
const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
const u32 offset = Memory::Read_U32(request.in_vectors[1].address);
const auto mode = static_cast<SeekMode>(Memory::Read_U32(request.in_vectors[2].address));
return IPCReply(SeekContent(cfd, offset, mode, uid));
return SeekContent(cfd, offset, mode, uid, ticks);
});
}
} // namespace IOS::HLE

View File

@ -793,7 +793,7 @@ ReturnCode ESDevice::ExportContentEnd(Context& context, u32 content_fd)
{
if (!context.title_import_export.valid || !context.title_import_export.content.valid)
return ES_EINVAL;
return CloseContent(content_fd, 0);
return static_cast<ReturnCode>(CloseContent(content_fd, 0));
}
IPCReply ESDevice::ExportContentEnd(Context& context, const IOCtlVRequest& request)