Initial commit

This commit is contained in:
InoriRus
2021-12-01 19:29:27 +10:00
parent b1e7dcdc5d
commit 43f49c8763
1843 changed files with 1111694 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
#include "Emulator/Config.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/MagicEnum.h"
#include "Kyty/Scripts/Scripts.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Config {
struct Config
{
uint32_t screen_width = 1280;
uint32_t screen_height = 720;
bool neo = true;
bool vulkan_validation_enabled = false;
bool shader_validation_enabled = false;
ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::None;
ShaderLogDirection shader_log_direction = ShaderLogDirection::Silent;
String shader_log_folder = U"_Shaders";
bool command_buffer_dump_enabled = false;
String command_buffer_dump_folder = U"_Buffers";
Log::Direction printf_direction = Log::Direction::Console;
String printf_output_file = U"_kyty.txt";
ProfilerDirection profiler_direction = ProfilerDirection::None;
String profiler_output_file = U"_profile.prof";
};
static Config* g_config = nullptr;
KYTY_SUBSYSTEM_INIT(Config)
{
EXIT_IF(g_config != nullptr);
g_config = new Config;
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Config) {}
KYTY_SUBSYSTEM_DESTROY(Config) {}
template <class T>
void LoadInt(T& dst, const Scripts::ScriptVar& cfg, const String& key)
{
auto var = cfg.At(key);
if (!var.IsNil())
{
dst = static_cast<T>(var.ToInteger());
}
}
void LoadBool(bool& dst, const Scripts::ScriptVar& cfg, const String& key)
{
auto var = cfg.At(key);
if (!var.IsNil())
{
dst = var.ToBool();
}
}
template <class T>
void LoadEnum(T& dst, const Scripts::ScriptVar& cfg, const String& key)
{
auto var = cfg.At(key);
if (!var.IsNil())
{
dst = Core::EnumValue(var.ToString(), dst);
}
}
void LoadStr(String& dst, const Scripts::ScriptVar& cfg, const String& key)
{
auto var = cfg.At(key);
if (!var.IsNil())
{
dst = var.ToString();
}
}
void Load(const Scripts::ScriptVar& cfg)
{
LoadInt(g_config->screen_width, cfg, U"ScreenWidth");
LoadInt(g_config->screen_height, cfg, U"ScreenHeight");
LoadBool(g_config->neo, cfg, U"Neo");
LoadBool(g_config->vulkan_validation_enabled, cfg, U"VulkanValidationEnabled");
LoadBool(g_config->shader_validation_enabled, cfg, U"ShaderValidationEnabled");
LoadEnum(g_config->shader_optimization_type, cfg, U"ShaderOptimizationType");
LoadEnum(g_config->shader_log_direction, cfg, U"ShaderLogDirection");
LoadStr(g_config->shader_log_folder, cfg, U"ShaderLogFolder");
LoadBool(g_config->command_buffer_dump_enabled, cfg, U"CommandBufferDumpEnabled");
LoadStr(g_config->command_buffer_dump_folder, cfg, U"CommandBufferDumpFolder");
LoadEnum(g_config->printf_direction, cfg, U"PrintfDirection");
LoadStr(g_config->printf_output_file, cfg, U"PrintfOutputFile");
LoadEnum(g_config->profiler_direction, cfg, U"ProfilerDirection");
LoadStr(g_config->profiler_output_file, cfg, U"ProfilerOutputFile");
}
uint32_t GetScreenWidth()
{
return g_config->screen_width;
}
uint32_t GetScreenHeight()
{
return g_config->screen_height;
}
bool IsNeo()
{
return g_config->neo;
}
bool VulkanValidationEnabled()
{
return g_config->vulkan_validation_enabled;
}
bool ShaderValidationEnabled()
{
return g_config->shader_validation_enabled;
}
ShaderOptimizationType GetShaderOptimizationType()
{
return g_config->shader_optimization_type;
}
ShaderLogDirection GetShaderLogDirection()
{
return g_config->shader_log_direction;
}
String GetShaderLogFolder()
{
return g_config->shader_log_folder;
}
bool CommandBufferDumpEnabled()
{
return g_config->command_buffer_dump_enabled;
}
String GetCommandBufferDumpFolder()
{
return g_config->command_buffer_dump_folder;
}
Log::Direction GetPrintfDirection()
{
return g_config->printf_direction;
}
String GetPrintfOutputFile()
{
return g_config->printf_output_file;
}
ProfilerDirection GetProfilerDirection()
{
return g_config->profiler_direction;
}
String GetProfilerOutputFile()
{
return g_config->profiler_output_file;
}
} // namespace Kyty::Config
#endif // KYTY_EMU_ENABLED
+438
View File
@@ -0,0 +1,438 @@
#include "Emulator/Controller.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Threads.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Kernel/Pthread.h"
#include "Emulator/Libs/Errno.h"
#include "Emulator/Libs/Libs.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Controller {
LIB_NAME("Pad", "Pad");
struct PadData
{
uint32_t buttons;
uint8_t left_stick_x;
uint8_t left_stick_y;
uint8_t right_stick_x;
uint8_t right_stick_y;
uint8_t analog_buttons_l2;
uint8_t analog_buttons_r2;
uint8_t padding[2];
float orientation_x;
float orientation_y;
float orientation_z;
float orientation_w;
float acceleration_x;
float acceleration_y;
float acceleration_z;
float angular_velocity_x;
float angular_velocity_y;
float angular_velocity_z;
uint8_t touch_data_touch_num;
uint8_t touch_data_reserve[3];
uint32_t touch_data_reserve1;
uint16_t touch_data_touch0_x;
uint16_t touch_data_touch0_y;
uint8_t touch_data_touch0_id;
uint8_t touch_data_touch0_reserve[3];
uint16_t touch_data_touch1_x;
uint16_t touch_data_touch1_y;
uint8_t touch_data_touch1_id;
uint8_t touch_data_touch1_reserve[3];
bool connected;
uint64_t timestamp;
uint32_t extension_unit_data_extension_unit_id;
uint8_t extension_unit_data_reserve[1];
uint8_t extension_unit_data_data_length;
uint8_t extension_unit_data_data[10];
uint8_t connected_count;
uint8_t reserve[2];
uint8_t device_unique_data_len;
uint8_t device_unique_data[12];
};
struct PadControllerInformation
{
float touch_pixel_density;
uint16_t touch_resolution_x;
uint16_t touch_resolution_y;
uint8_t stick_dead_zone_left;
uint8_t stick_dead_zone_right;
uint8_t connection_type;
uint8_t connected_count;
bool connected;
int device_class;
};
struct ControllerState
{
uint64_t time = 0;
uint32_t buttons = 0;
int axes[static_cast<int>(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0};
};
class GameController
{
public:
GameController() = default;
virtual ~GameController() = default;
KYTY_CLASS_NO_COPY(GameController);
void Connect(int id);
void Disconnect(int id);
void Button(int id, uint32_t button, bool down);
void Axis(int id, Axis axis, int value);
void GetConnectionInfo(bool* flag, int* count);
void ReadState(ControllerState* state, bool* flag, int* count);
private:
static constexpr uint32_t STATES_MAX = 64;
void CheckActive();
[[nodiscard]] ControllerState GetLastState() const;
void AddState(const ControllerState& state);
Core::Mutex m_mutex;
Vector<int> m_connected_ids;
int m_active_id = -1;
bool m_connected = false;
int m_connected_count = 0;
ControllerState m_states[STATES_MAX];
ControllerState m_last_state;
uint32_t m_states_num = 0;
uint32_t m_first_state = 0;
};
static GameController* g_controller = nullptr;
KYTY_SUBSYSTEM_INIT(Controller)
{
EXIT_IF(g_controller != nullptr);
g_controller = new GameController;
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Controller) {}
KYTY_SUBSYSTEM_DESTROY(Controller) {}
void GameController::Connect(int id)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(m_connected_ids.Contains(id));
m_connected_ids.Add(id);
CheckActive();
}
void GameController::Disconnect(int id)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(!m_connected_ids.Contains(id));
m_connected_ids.Remove(id);
CheckActive();
}
void GameController::CheckActive()
{
bool reset = false;
if (m_connected)
{
if (m_connected_ids.IsEmpty())
{
m_active_id = -1;
m_connected = false;
reset = true;
} else
{
if (m_connected_ids.At(0) != m_active_id)
{
m_active_id = m_connected_ids.At(0);
m_connected_count++;
reset = true;
}
}
} else
{
if (!m_connected_ids.IsEmpty())
{
m_active_id = m_connected_ids.At(0);
m_connected = true;
m_connected_count++;
reset = true;
}
}
if (reset)
{
m_states_num = 0;
m_last_state = ControllerState();
}
}
ControllerState GameController::GetLastState() const
{
if (m_states_num == 0)
{
return m_last_state;
}
auto last = (m_first_state + m_states_num - 1) % STATES_MAX;
return m_states[last];
}
void GameController::AddState(const ControllerState& state)
{
if (m_states_num >= STATES_MAX)
{
m_states_num = STATES_MAX - 1;
m_first_state = (m_first_state + 1) % STATES_MAX;
}
m_states[(m_first_state + m_states_num) % STATES_MAX] = state;
m_last_state = state;
m_states_num++;
}
void GameController::Button(int id, uint32_t button, bool down)
{
Core::LockGuard lock(m_mutex);
if (m_active_id == id)
{
auto state = GetLastState();
state.time = LibKernel::KernelGetProcessTime();
if (down)
{
state.buttons |= button;
} else
{
state.buttons &= ~button;
}
AddState(state);
}
}
void GameController::Axis(int id, Controller::Axis axis, int value)
{
Core::LockGuard lock(m_mutex);
if (m_active_id == id)
{
auto state = GetLastState();
state.time = LibKernel::KernelGetProcessTime();
int axis_id = static_cast<int>(axis);
EXIT_IF(axis_id < 0 || axis_id >= static_cast<int>(Controller::Axis::AxisMax));
state.axes[axis_id] = value;
if (axis == Controller::Axis::TriggerLeft)
{
if (value > 0)
{
state.buttons |= PAD_BUTTON_L2;
} else
{
state.buttons &= ~PAD_BUTTON_L2;
}
}
if (axis == Controller::Axis::TriggerRight)
{
if (value > 0)
{
state.buttons |= PAD_BUTTON_R2;
} else
{
state.buttons &= ~PAD_BUTTON_R2;
}
}
AddState(state);
}
}
void GameController::GetConnectionInfo(bool* flag, int* count)
{
EXIT_IF(flag == nullptr);
EXIT_IF(count == nullptr);
Core::LockGuard lock(m_mutex);
*flag = m_connected;
*count = m_connected_count;
}
void GameController::ReadState(ControllerState* state, bool* flag, int* count)
{
EXIT_IF(flag == nullptr);
EXIT_IF(count == nullptr);
EXIT_IF(state == nullptr);
Core::LockGuard lock(m_mutex);
*flag = m_connected;
*count = m_connected_count;
*state = GetLastState();
}
void ControllerConnect(int id)
{
EXIT_IF(g_controller == nullptr);
g_controller->Connect(id);
}
void ControllerDisconnect(int id)
{
EXIT_IF(g_controller == nullptr);
g_controller->Disconnect(id);
}
void ControllerButton(int id, uint32_t button, bool down)
{
EXIT_IF(g_controller == nullptr);
g_controller->Button(id, button, down);
}
void ControllerAxis(int id, Axis axis, int value)
{
EXIT_IF(g_controller == nullptr);
g_controller->Axis(id, axis, value);
}
int KYTY_SYSV_ABI PadInit()
{
PRINT_NAME();
return OK;
}
int KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(user_id != 1);
EXIT_NOT_IMPLEMENTED(type != 0);
EXIT_NOT_IMPLEMENTED(index != 0);
EXIT_NOT_IMPLEMENTED(param != nullptr);
int handle = 1;
return handle;
}
int KYTY_SYSV_ABI PadSetMotionSensorState(int handle, bool enable)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(handle != 1);
printf("\t enable = %s\n", (enable ? "true" : "false"));
return OK;
}
int KYTY_SYSV_ABI PadGetControllerInformation(int handle, PadControllerInformation* info)
{
PRINT_NAME();
EXIT_IF(g_controller == nullptr);
int connected_count = 0;
bool connected = false;
g_controller->GetConnectionInfo(&connected, &connected_count);
EXIT_NOT_IMPLEMENTED(handle != 1);
EXIT_NOT_IMPLEMENTED(info == nullptr);
info->touch_pixel_density = 44.86f;
info->touch_resolution_x = 1920;
info->touch_resolution_y = 943;
info->stick_dead_zone_left = controller_get_axis(-32768, 32767, 8000) - 128;
info->stick_dead_zone_right = controller_get_axis(-32768, 32767, 8000) - 128;
info->connection_type = 0;
info->connected_count = (connected_count > 255 ? 255 : connected_count);
info->connected = connected;
info->device_class = 0;
return OK;
}
int KYTY_SYSV_ABI PadReadState(int handle, PadData* data)
{
PRINT_NAME();
EXIT_IF(g_controller == nullptr);
int connected_count = 0;
bool connected = false;
ControllerState state;
g_controller->ReadState(&state, &connected, &connected_count);
EXIT_NOT_IMPLEMENTED(handle != 1);
EXIT_NOT_IMPLEMENTED(data == nullptr);
data->buttons = state.buttons;
data->left_stick_x = state.axes[static_cast<int>(Axis::LeftX)];
data->left_stick_y = state.axes[static_cast<int>(Axis::LeftY)];
data->right_stick_x = state.axes[static_cast<int>(Axis::RightX)];
data->right_stick_y = state.axes[static_cast<int>(Axis::RightY)];
data->analog_buttons_l2 = state.axes[static_cast<int>(Axis::TriggerLeft)];
data->analog_buttons_r2 = state.axes[static_cast<int>(Axis::TriggerRight)];
data->orientation_x = 0.0f;
data->orientation_y = 0.0f;
data->orientation_z = 0.0f;
data->orientation_w = 1.0f;
data->acceleration_x = 0.0f;
data->acceleration_y = 0.0f;
data->acceleration_z = 0.0f;
data->angular_velocity_x = 0.0f;
data->angular_velocity_y = 0.0f;
data->angular_velocity_z = 0.0f;
data->touch_data_touch_num = 0;
data->touch_data_touch0_x = 0;
data->touch_data_touch0_y = 0;
data->touch_data_touch0_id = 1;
data->touch_data_touch1_x = 0;
data->touch_data_touch1_y = 0;
data->touch_data_touch1_id = 2;
data->connected = connected;
data->timestamp = state.time;
data->connected_count = connected_count;
data->device_unique_data_len = 0;
return OK;
}
} // namespace Kyty::Libs::Controller
#endif // KYTY_EMU_ENABLED
+461
View File
@@ -0,0 +1,461 @@
#include "Emulator/Elf.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/File.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Loader {
static Elf64_Ehdr* load_ehdr_64(Core::File& f)
{
auto* ehdr = new Elf64_Ehdr;
f.Read(ehdr, sizeof(Elf64_Ehdr));
return ehdr;
}
static Elf64_Phdr* load_phdr_64(Core::File& f, uint64_t offset, Elf64_Half num)
{
auto* phdr = new Elf64_Phdr[num];
f.Seek(offset);
f.Read(phdr, sizeof(Elf64_Phdr) * num);
return phdr;
}
static Elf64_Shdr* load_shdr_64(Core::File& f, uint64_t offset, Elf64_Half num)
{
if (num == 0)
{
return nullptr;
}
auto* shdr = new Elf64_Shdr[num];
f.Seek(offset);
f.Read(shdr, sizeof(Elf64_Shdr) * num);
return shdr;
}
static void* load_dynamic_64(Core::File& f, uint64_t offset, uint64_t size)
{
void* dynamic_data = new uint8_t[size];
f.Seek(offset);
f.Read(dynamic_data, size);
return dynamic_data;
}
static char* load_str_table(Core::File& f, uint64_t offset, uint32_t size)
{
auto* str_table = new char[size];
f.Seek(offset);
f.Read(str_table, size);
return str_table;
}
static void dbg_print_ehdr_64(Elf64_Ehdr* ehdr, Core::File& f)
{
f.Printf("ehdr->e_ident = ");
for (auto i: ehdr->e_ident)
{
f.Printf("%02x", i);
}
f.Printf("\n");
f.Printf("ehdr->e_type = 0x%04" PRIx16 "\n", ehdr->e_type);
f.Printf("ehdr->e_machine = 0x%04" PRIx16 "\n", ehdr->e_machine);
f.Printf("ehdr->e_version = 0x%08" PRIx32 "\n", ehdr->e_version);
f.Printf("ehdr->e_entry = 0x%016" PRIx64 "\n", ehdr->e_entry);
f.Printf("ehdr->e_phoff = 0x%016" PRIx64 "\n", ehdr->e_phoff);
f.Printf("ehdr->e_shoff = 0x%016" PRIx64 "\n", ehdr->e_shoff);
f.Printf("ehdr->e_flags = 0x%08" PRIx32 "\n", ehdr->e_flags);
f.Printf("ehdr->e_ehsize = 0x%04" PRIx16 "\n", ehdr->e_ehsize);
f.Printf("ehdr->e_phentsize = 0x%04" PRIx16 "\n", ehdr->e_phentsize);
f.Printf("ehdr->e_phnum = %" PRIu16 "\n", ehdr->e_phnum);
f.Printf("ehdr->e_shentsize = 0x%04" PRIx16 "\n", ehdr->e_shentsize);
f.Printf("ehdr->e_shnum = %" PRIu16 "\n", ehdr->e_shnum);
f.Printf("ehdr->e_shstrndx = %" PRIu16 "\n", ehdr->e_shstrndx);
}
static void dbg_print_phdr_64(Elf64_Phdr* phdr, Core::File& f)
{
f.Printf("phdr->p_type = 0x%08" PRIx32 "\n", phdr->p_type);
f.Printf("phdr->p_flags = 0x%08" PRIx32 "\n", phdr->p_flags);
f.Printf("phdr->p_offset = 0x%016" PRIx64 "\n", phdr->p_offset);
f.Printf("phdr->p_vaddr = 0x%016" PRIx64 "\n", phdr->p_vaddr);
f.Printf("phdr->p_paddr = 0x%016" PRIx64 "\n", phdr->p_paddr);
f.Printf("phdr->p_filesz = 0x%016" PRIx64 "\n", phdr->p_filesz);
f.Printf("phdr->p_memsz = 0x%016" PRIx64 "\n", phdr->p_memsz);
f.Printf("phdr->p_align = 0x%016" PRIx64 "\n", phdr->p_align);
}
static void dbg_print_shdr_64(Elf64_Shdr* shdr, Core::File& f)
{
f.Printf("shdr->sh_name = %d\n", shdr->sh_name);
f.Printf("shdr->sh_type = 0x%08" PRIx32 "\n", shdr->sh_type);
f.Printf("shdr->sh_flags = 0x%016" PRIx64 "\n", shdr->sh_flags);
f.Printf("shdr->sh_addr = 0x%016" PRIx64 "\n", shdr->sh_addr);
f.Printf("shdr->sh_offset = 0x%016" PRIx64 "\n", shdr->sh_offset);
f.Printf("shdr->sh_size = 0x%016" PRIx64 "\n", shdr->sh_size);
f.Printf("shdr->sh_link = %" PRId32 "\n", shdr->sh_link);
f.Printf("shdr->sh_info = 0x%08" PRIx32 "\n", shdr->sh_info);
f.Printf("shdr->sh_addralign = 0x%016" PRIx64 "\n", shdr->sh_addralign);
f.Printf("shdr->sh_entsize = 0x%016" PRIx64 "\n", shdr->sh_entsize);
}
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define DBG_NAME(tag) \
case tag: name = #tag; break;
static void dbg_print_dynamic_64(const Elf64_Dyn* dyn, Core::File& f)
{
const char* name = "Unknown";
switch (dyn->d_tag)
{
DBG_NAME(DT_OS_HASH)
DBG_NAME(DT_HASH)
DBG_NAME(DT_OS_STRTAB)
DBG_NAME(DT_OS_STRSZ)
DBG_NAME(DT_STRTAB)
DBG_NAME(DT_STRSZ)
DBG_NAME(DT_OS_SYMTAB)
DBG_NAME(DT_SYMTAB)
DBG_NAME(DT_OS_HASHSZ)
DBG_NAME(DT_OS_SYMTABSZ)
DBG_NAME(DT_INIT)
DBG_NAME(DT_FINI)
DBG_NAME(DT_OS_PLTGOT)
DBG_NAME(DT_PLTGOT)
DBG_NAME(DT_OS_JMPREL)
DBG_NAME(DT_JMPREL)
DBG_NAME(DT_OS_PLTRELSZ)
DBG_NAME(DT_PLTRELSZ)
DBG_NAME(DT_OS_PLTREL)
DBG_NAME(DT_PLTREL)
DBG_NAME(DT_OS_RELA)
DBG_NAME(DT_RELA)
DBG_NAME(DT_OS_RELASZ)
DBG_NAME(DT_RELASZ)
DBG_NAME(DT_OS_RELAENT)
DBG_NAME(DT_RELAENT)
DBG_NAME(DT_INIT_ARRAY)
DBG_NAME(DT_INIT_ARRAYSZ)
DBG_NAME(DT_FINI_ARRAY)
DBG_NAME(DT_FINI_ARRAYSZ)
DBG_NAME(DT_PREINIT_ARRAY)
DBG_NAME(DT_PREINIT_ARRAYSZ)
DBG_NAME(DT_OS_SYMENT)
DBG_NAME(DT_SYMENT)
DBG_NAME(DT_DEBUG)
DBG_NAME(DT_TEXTREL)
DBG_NAME(DT_FLAGS)
DBG_NAME(DT_NEEDED)
DBG_NAME(DT_OS_NEEDED_MODULE)
DBG_NAME(DT_OS_NEEDED_MODULE_1)
DBG_NAME(DT_OS_IMPORT_LIB)
DBG_NAME(DT_OS_IMPORT_LIB_1)
DBG_NAME(DT_OS_IMPORT_LIB_ATTR)
DBG_NAME(DT_OS_FINGERPRINT)
DBG_NAME(DT_OS_ORIGINAL_FILENAME)
DBG_NAME(DT_OS_ORIGINAL_FILENAME_1)
DBG_NAME(DT_OS_MODULE_INFO)
DBG_NAME(DT_OS_MODULE_INFO_1)
DBG_NAME(DT_OS_MODULE_ATTR)
DBG_NAME(DT_SONAME)
DBG_NAME(DT_OS_EXPORT_LIB)
DBG_NAME(DT_OS_EXPORT_LIB_1)
DBG_NAME(DT_OS_EXPORT_LIB_ATTR)
DBG_NAME(DT_RELACOUNT)
DBG_NAME(DT_NULL)
}
f.Printf("d_tag = 0x%016" PRIx64 ", d_val = 0x%016" PRIx64 ", name = %s\n", dyn->d_tag, dyn->d_un.d_val, name);
}
Elf64::~Elf64()
{
Clear();
}
void Elf64::LoadSegment(uint64_t vaddr, uint64_t file_offset, uint64_t size)
{
EXIT_IF(m_f == nullptr);
m_f->Seek(file_offset);
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), size);
}
const Elf64_Dyn* Elf64::GetDynValue(Elf64_Sxword tag) const
{
for (const auto* dyn = GetDynamic(); dyn->d_tag != DT_NULL; dyn++)
{
if (dyn->d_tag == tag)
{
return dyn;
}
}
return nullptr;
}
Vector<const Elf64_Dyn*> Elf64::GetDynList(Elf64_Sxword tag) const
{
Vector<const Elf64_Dyn*> ret;
for (const auto* dyn = GetDynamic(); dyn->d_tag != DT_NULL; dyn++)
{
if (dyn->d_tag == tag)
{
ret.Add(dyn);
}
}
return ret;
}
bool Elf64::IsShared() const
{
return (m_ehdr->e_type == ET_DYNAMIC);
}
bool Elf64::IsNextGen() const
{
return (m_ehdr->e_ident[EI_ABIVERSION] == 2);
}
void Elf64::Clear()
{
if (m_f != nullptr)
{
m_f->Close();
delete m_f;
}
delete m_ehdr;
delete[] m_phdr;
delete[] m_shdr;
delete[] m_str_table;
delete[] static_cast<uint8_t*>(m_dynamic);
delete[] static_cast<uint8_t*>(m_dynamic_data);
m_ehdr = nullptr;
m_phdr = nullptr;
m_shdr = nullptr;
m_str_table = nullptr;
m_dynamic = nullptr;
m_dynamic_data = nullptr;
}
void Elf64::DbgDump(const String& folder)
{
auto folder_str = folder.FixDirectorySlash();
Core::File::CreateDirectories(folder_str);
for (uint16_t i = 0; i < m_ehdr->e_phnum; i++)
{
if (m_phdr[i].p_filesz == 0u)
{
continue;
}
char str[512];
sprintf(str, "phdr_%03d", i);
Core::File fout;
fout.Create(folder_str + str);
auto* buf = new char[static_cast<uint32_t>(m_phdr[i].p_filesz)];
m_f->Seek(m_phdr[i].p_offset);
m_f->Read(buf, static_cast<uint32_t>(m_phdr[i].p_filesz));
fout.Write(buf, static_cast<uint32_t>(m_phdr[i].p_filesz));
delete[] buf;
fout.Close();
}
for (uint16_t i = 0; i < m_ehdr->e_shnum; i++)
{
if (m_shdr[i].sh_size == 0u)
{
continue;
}
char str[512];
sprintf(str, "shdr_%03d", i);
Core::File fout;
fout.Create(folder_str + str);
auto* buf = new char[static_cast<uint32_t>(m_shdr[i].sh_size)];
m_f->Seek(m_shdr[i].sh_offset);
m_f->Read(buf, static_cast<uint32_t>(m_shdr[i].sh_size));
fout.Write(buf, static_cast<uint32_t>(m_shdr[i].sh_size));
delete[] buf;
fout.Close();
}
Core::File fout;
fout.Create(folder_str + U"ehdr.txt");
dbg_print_ehdr_64(m_ehdr, fout);
fout.Close();
fout.Create(folder_str + U"phdr.txt");
for (uint16_t i = 0; i < m_ehdr->e_phnum; i++)
{
fout.Printf("--- phdr [%d] ---\n", i);
dbg_print_phdr_64(m_phdr + i, fout);
}
fout.Close();
fout.Create(folder_str + U"shdr.txt");
for (uint16_t i = 0; i < m_ehdr->e_shnum; i++)
{
fout.Printf("--- shdr [%d] %s ---\n", i, GetSectionName(i));
dbg_print_shdr_64(m_shdr + i, fout);
}
fout.Close();
fout.Create(folder_str + U"dynamic.txt");
for (const auto* dyn = GetDynamic(); dyn->d_tag != DT_NULL; dyn++)
{
dbg_print_dynamic_64(dyn, fout);
}
fout.Close();
}
uint64_t Elf64::GetEntry()
{
return m_ehdr->e_entry;
}
bool Elf64::IsValid() const
{
bool ret = true;
if (m_f == nullptr || m_f->IsInvalid())
{
return false;
}
if (m_ehdr == nullptr)
{
return false;
}
if (m_ehdr->e_ident[EI_MAG0] != '\x7f' || m_ehdr->e_ident[EI_MAG1] != 'E' || m_ehdr->e_ident[EI_MAG2] != 'L' ||
m_ehdr->e_ident[EI_MAG3] != 'F')
{
printf("Not an ELF file\n");
return false;
}
if (m_ehdr->e_ident[EI_CLASS] != ELFCLASS64)
{
printf("ehdr->e_ident[EI_CLASS] (0x%x) != ELFCLASS64\n", m_ehdr->e_ident[EI_CLASS]);
return false;
}
if (m_ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
{
printf("ehdr->e_ident[EI_DATA] (0x%x) != ELFDATA2LSB\n", m_ehdr->e_ident[EI_DATA]);
return false;
}
if (m_ehdr->e_ident[EI_VERSION] != EV_CURRENT)
{
printf("ehdr->e_ident[EI_VERSION] != EV_CURRENT\n");
return false;
}
if (m_ehdr->e_ident[EI_OSABI] != ELFOSABI_FREEBSD)
{
printf("ehdr->e_ident[EI_OSABI] (0x%x) != ELFOSABI_FREEBSD\n", m_ehdr->e_ident[EI_OSABI]);
return false;
}
if (m_ehdr->e_ident[EI_ABIVERSION] != 0 && m_ehdr->e_ident[EI_ABIVERSION] != 2)
{
printf("ehdr->e_ident[EI_ABIVERSION] (0x%x) != (0 or 2)\n", m_ehdr->e_ident[EI_ABIVERSION]);
return false;
}
if (m_ehdr->e_type != ET_DYNEXEC && m_ehdr->e_type != ET_DYNAMIC)
{
printf("ehdr->e_type (%04x) != ET_DYNEXEC && m_ehdr->e_type != ET_DYNAMIC\n", m_ehdr->e_type);
return false;
}
if (m_ehdr->e_machine != EM_X86_64)
{
printf("ehdr->e_machine (%04x) != EM_X86_64\n", m_ehdr->e_machine);
return false;
}
if (m_ehdr->e_version != EV_CURRENT)
{
printf("ehdr->e_version != EV_CURRENT\n");
return false;
}
if (m_ehdr->e_phentsize != sizeof(Elf64_Phdr))
{
printf("ehdr->e_phentsize != sizeof(Elf64_Phdr)\n");
return false;
}
if (m_ehdr->e_shentsize > 0 && m_ehdr->e_shentsize != sizeof(Elf64_Shdr))
{
printf("ehdr->e_shentsize (%d) != sizeof(Elf64_Shdr)\n", m_ehdr->e_shentsize);
return false;
}
return ret;
}
void Elf64::Open(const String& file_name)
{
Clear();
m_f = new Core::File;
m_f->Open(file_name, Core::File::Mode::Read);
if (m_f->IsInvalid())
{
EXIT("Can't open %s\n", file_name.C_Str());
}
m_ehdr = load_ehdr_64(*m_f);
m_phdr = load_phdr_64(*m_f, m_ehdr->e_phoff, m_ehdr->e_phnum);
m_shdr = load_shdr_64(*m_f, m_ehdr->e_shoff, m_ehdr->e_shnum);
if (m_shdr != nullptr)
{
m_str_table = load_str_table(*m_f, m_shdr[m_ehdr->e_shstrndx].sh_offset, static_cast<uint32_t>(m_shdr[m_ehdr->e_shstrndx].sh_size));
}
for (Elf64_Half i = 0; i < m_ehdr->e_phnum; i++)
{
if (m_phdr[i].p_type == PT_DYNAMIC)
{
m_dynamic = load_dynamic_64(*m_f, m_phdr[i].p_offset, m_phdr[i].p_filesz);
}
if (m_phdr[i].p_type == PT_OS_DYNLIBDATA)
{
m_dynamic_data = load_dynamic_64(*m_f, m_phdr[i].p_offset, m_phdr[i].p_filesz);
}
}
}
} // namespace Kyty::Loader
#endif // KYTY_EMU_ENABLED
+24
View File
@@ -0,0 +1,24 @@
#include "Emulator/Emulator.h"
#include "Kyty/Core/Subsystems.h"
#include "Emulator/Common.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Emulator {
void kyty_reg();
KYTY_SUBSYSTEM_INIT(Emulator)
{
kyty_reg();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Emulator) {}
KYTY_SUBSYSTEM_DESTROY(Emulator) {}
} // namespace Kyty::Emulator
#endif // KYTY_EMU_ENABLED
@@ -0,0 +1,148 @@
#include "Emulator/Graphics/DepthStencilBuffer.h"
#include "Kyty/Core/DbgAssert.h"
#include "Emulator/Graphics/GraphicContext.h"
#include "Emulator/Graphics/GraphicsRender.h"
#include "Emulator/Graphics/Utils.h"
#include "Emulator/Profiler.h"
#include <vulkan/vulkan_core.h>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
void* DepthStencilBufferObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num,
VulkanMemory* mem) const
{
KYTY_PROFILER_BLOCK("DepthStencilBufferObject::Create");
EXIT_IF(size == nullptr || vaddr == nullptr);
EXIT_IF(mem == nullptr);
EXIT_IF(ctx == nullptr);
auto pixel_format = static_cast<VkFormat>(params[PARAM_FORMAT]);
auto width = params[PARAM_WIDTH];
auto height = params[PARAM_HEIGHT];
EXIT_NOT_IMPLEMENTED(pixel_format == VK_FORMAT_UNDEFINED);
EXIT_NOT_IMPLEMENTED(width == 0);
EXIT_NOT_IMPLEMENTED(height == 0);
auto* vk_obj = new DepthStencilVulkanImage;
vk_obj->extent.width = width;
vk_obj->extent.height = height;
vk_obj->format = pixel_format;
vk_obj->image = nullptr;
vk_obj->image_view = nullptr;
VkImageCreateInfo image_info {};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.pNext = nullptr;
image_info.flags = 0;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.extent.width = vk_obj->extent.width;
image_info.extent.height = vk_obj->extent.height;
image_info.extent.depth = 1;
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.format = vk_obj->format;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
vkCreateImage(ctx->device, &image_info, nullptr, &vk_obj->image);
EXIT_NOT_IMPLEMENTED(vk_obj->image == nullptr);
vkGetImageMemoryRequirements(ctx->device, vk_obj->image, &mem->requirements);
mem->property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
bool allocated = VulkanAllocate(ctx, mem);
EXIT_NOT_IMPLEMENTED(!allocated);
VulkanBindImageMemory(ctx, vk_obj, mem);
vk_obj->memory = *mem;
// EXIT_NOT_IMPLEMENTED(mem->requirements.size > *size);
GetUpdateFunc()(ctx, params, vk_obj, vaddr, size, vaddr_num);
VkImageViewCreateInfo create_info {};
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
create_info.pNext = nullptr;
create_info.flags = 0;
create_info.image = vk_obj->image;
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
create_info.format = vk_obj->format;
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
create_info.subresourceRange.baseArrayLayer = 0;
create_info.subresourceRange.baseMipLevel = 0;
create_info.subresourceRange.layerCount = 1;
create_info.subresourceRange.levelCount = 1;
vkCreateImageView(ctx->device, &create_info, nullptr, &vk_obj->image_view);
EXIT_NOT_IMPLEMENTED(vk_obj->image_view == nullptr);
UtilSetImageLayoutOptimal(vk_obj);
return vk_obj;
}
static void update_func(GraphicContext* /*ctx*/, const uint64_t* /*params*/, void* /*obj*/, const uint64_t* /*vaddr*/,
const uint64_t* /*size*/, int /*vaddr_num*/)
{
KYTY_PROFILER_BLOCK("DepthStencilBufferObject::update_func");
}
bool DepthStencilBufferObject::Equal(const uint64_t* other) const
{
return (params[PARAM_FORMAT] == other[PARAM_FORMAT] && params[PARAM_WIDTH] == other[PARAM_WIDTH] &&
params[PARAM_HEIGHT] == other[PARAM_HEIGHT] && params[PARAM_HTILE] == other[PARAM_HTILE]);
}
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* mem)
{
KYTY_PROFILER_BLOCK("DepthStencilBufferObject::delete_func");
auto* vk_obj = reinterpret_cast<DepthStencilVulkanImage*>(obj);
EXIT_IF(vk_obj == nullptr);
EXIT_IF(ctx == nullptr);
DeleteFramebuffer(vk_obj);
vkDestroyImageView(ctx->device, vk_obj->image_view, nullptr);
vkDestroyImage(ctx->device, vk_obj->image, nullptr);
VulkanFree(ctx, mem);
delete vk_obj;
}
GpuObject::delete_func_t DepthStencilBufferObject::GetDeleteFunc() const
{
return delete_func;
}
GpuObject::update_func_t DepthStencilBufferObject::GetUpdateFunc() const
{
return update_func;
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED
+923
View File
@@ -0,0 +1,923 @@
#include "Emulator/Graphics/GpuMemory.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/MagicEnum.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Threads.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Graphics/GraphicContext.h"
#include "Emulator/Profiler.h"
#include <algorithm>
#include <atomic>
#include <vulkan/vulkan_core.h>
//#define XXH_INLINE_ALL
#include <xxhash/xxhash.h>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
class GpuMemory
{
public:
GpuMemory() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
virtual ~GpuMemory() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(GpuMemory);
bool IsAllocated(uint64_t vaddr, uint64_t size);
void SetAllocatedRange(uint64_t vaddr, uint64_t size);
void Free(GraphicContext* ctx, uint64_t vaddr, uint64_t size);
void* GetObject(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, const GpuObject& info);
void ResetHash(GraphicContext* ctx, uint64_t* vaddr, uint64_t* size, int vaddr_num, GpuMemoryObjectType type);
void FrameDone();
void WriteBack(GraphicContext* ctx);
void DbgDump();
private:
static constexpr int OBJ_OVERLAPS_MAX = 2;
static constexpr int VADDR_BLOCKS_MAX = 3;
struct AllocatedRange
{
uint64_t vaddr;
uint64_t size;
};
struct ObjectInfo
{
void* obj = nullptr;
uint64_t params[GpuObject::PARAMS_MAX] = {};
GpuMemoryObjectType type = GpuMemoryObjectType::Invalid;
uint64_t hash[VADDR_BLOCKS_MAX] = {};
GpuObject::write_back_func_t write_back_func = nullptr;
GpuObject::delete_func_t delete_func = nullptr;
GpuObject::update_func_t update_func = nullptr;
uint64_t use_last_frame = 0;
uint64_t use_num = 0;
bool in_use = false;
bool read_only = false;
bool check_hash = false;
VulkanMemory mem;
};
struct Object
{
uint64_t vaddr[VADDR_BLOCKS_MAX] = {};
uint64_t size[VADDR_BLOCKS_MAX] = {};
int vaddr_num = 0;
ObjectInfo overlaps[OBJ_OVERLAPS_MAX];
int overlaps_num = 0;
bool free = true;
};
void Free(GraphicContext* ctx, Object& h);
Core::Mutex m_mutex;
Vector<AllocatedRange> m_allocated;
Vector<Object> m_objects;
uint64_t m_objects_size = 0;
uint64_t m_current_frame = 0;
};
class GpuResources
{
public:
struct Info
{
uint32_t owner = 0;
bool free = true;
uint64_t memory = 0;
size_t size = 0;
String name;
uint32_t type = 0;
uint64_t user_data = 0;
};
GpuResources() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
virtual ~GpuResources() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(GpuResources);
uint32_t AddOwner(const String& name);
uint32_t AddResource(uint32_t owner_handle, uint64_t memory, size_t size, const String& name, uint32_t type, uint64_t user_data);
void DeleteOwner(uint32_t owner_handle);
void DeleteResources(uint32_t owner_handle);
void DeleteResource(uint32_t resource_handle);
bool FindInfo(uint64_t memory, Info* dst);
private:
struct Owner
{
String name;
bool free = true;
};
Core::Mutex m_mutex;
Vector<Owner> m_owners;
Vector<Info> m_infos;
};
static GpuMemory* g_gpu_memory = nullptr;
static GpuResources* g_gpu_resources = nullptr;
uint32_t GpuResources::AddOwner(const String& name)
{
Core::LockGuard lock(m_mutex);
Owner n;
n.name = name;
n.free = false;
uint32_t index = 0;
for (auto& b: m_owners)
{
if (b.free)
{
b = n;
return index;
}
index++;
}
m_owners.Add(n);
return index;
}
uint32_t GpuResources::AddResource(uint32_t owner_handle, uint64_t memory, size_t size, const String& name, uint32_t type,
uint64_t user_data)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(!m_owners.IndexValid(owner_handle));
EXIT_NOT_IMPLEMENTED(memory == 0);
Info info;
info.owner = owner_handle;
info.memory = memory;
info.free = false;
info.name = name;
info.size = size;
info.type = type;
info.user_data = user_data;
uint32_t index = 0;
for (auto& i: m_infos)
{
if (i.free)
{
i = info;
return index;
}
index++;
}
m_infos.Add(info);
return index;
}
void GpuResources::DeleteOwner(uint32_t owner_handle)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(!m_owners.IndexValid(owner_handle));
for (auto& i: m_infos)
{
if (!i.free && i.owner == owner_handle)
{
i.free = true;
}
}
EXIT_NOT_IMPLEMENTED(m_owners[owner_handle].free);
m_owners[owner_handle].free = true;
}
void GpuResources::DeleteResources(uint32_t owner_handle)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(!m_owners.IndexValid(owner_handle));
for (auto& i: m_infos)
{
if (!i.free && i.owner == owner_handle)
{
i.free = true;
}
}
}
void GpuResources::DeleteResource(uint32_t resource_handle)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(!m_infos.IndexValid(resource_handle));
EXIT_NOT_IMPLEMENTED(m_infos[resource_handle].free);
m_infos[resource_handle].free = true;
}
bool GpuResources::FindInfo(uint64_t memory, Info* dst)
{
EXIT_IF(dst == nullptr);
Core::LockGuard lock(m_mutex);
// NOLINTNEXTLINE(readability-use-anyofallof)
for (const auto& i: m_infos)
{
if (!i.free && memory >= i.memory && memory < i.memory + i.size)
{
*dst = i;
return true;
}
}
return false;
}
void GpuMemory::SetAllocatedRange(uint64_t vaddr, uint64_t size)
{
EXIT_IF(size == 0);
EXIT_NOT_IMPLEMENTED(IsAllocated(vaddr, size));
Core::LockGuard lock(m_mutex);
AllocatedRange r {};
r.vaddr = vaddr;
r.size = size;
m_allocated.Add(r);
}
bool GpuMemory::IsAllocated(uint64_t vaddr, uint64_t size)
{
EXIT_IF(size == 0);
Core::LockGuard lock(m_mutex);
return std::any_of(m_allocated.begin(), m_allocated.end(),
[vaddr, size](auto& r) {
return ((vaddr >= r.vaddr && vaddr < r.vaddr + r.size) ||
((vaddr + size - 1) >= r.vaddr && (vaddr + size - 1) < r.vaddr + r.size));
});
}
static uint64_t calc_hash(const uint8_t* buf, uint64_t size)
{
KYTY_PROFILER_FUNCTION();
return (size > 0 && buf != nullptr ? XXH64(buf, size, 0) : 0);
}
static bool vaddr_equal(const uint64_t* vaddr, const uint64_t* size, int vaddr_num, const uint64_t* vaddr2, const uint64_t* size2,
int vaddr_num2)
{
if (vaddr_num != vaddr_num2)
{
return false;
}
for (int i = 0; i < vaddr_num; i++)
{
if (vaddr[i] != vaddr2[i] || size[i] != size2[i])
{
return false;
}
}
return true;
}
static bool vaddr_overlap(const uint64_t* hvaddr, const uint64_t* hsize, int vaddr_num, uint64_t vaddr, uint64_t size)
{
for (int i = 0; i < vaddr_num; i++)
{
if ((vaddr >= hvaddr[i] && vaddr < hvaddr[i] + hsize[i]) ||
((vaddr + size - 1) >= hvaddr[i] && (vaddr + size - 1) < hvaddr[i] + hsize[i]))
{
return true;
}
}
return false;
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
void* GpuMemory::GetObject(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, const GpuObject& info)
{
EXIT_IF(info.type == GpuMemoryObjectType::Invalid);
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num > VADDR_BLOCKS_MAX || vaddr_num <= 0);
Core::LockGuard lock(m_mutex);
uint64_t hash[VADDR_BLOCKS_MAX] = {};
for (int vi = 0; vi < vaddr_num; vi++)
{
EXIT_IF(size[vi] == 0);
if (info.check_hash)
{
hash[vi] = calc_hash(reinterpret_cast<const uint8_t*>(vaddr[vi]), size[vi]);
} else
{
hash[vi] = 0;
}
}
Object* update_object = nullptr;
for (auto& h: m_objects)
{
if (!h.free && vaddr_equal(h.vaddr, h.size, h.vaddr_num, vaddr, size, vaddr_num))
{
for (int oi = 0; oi < h.overlaps_num; oi++)
{
auto& o = h.overlaps[oi];
if (o.type == info.type && info.Equal(o.params))
{
bool need_update = false;
for (int vi = 0; vi < h.vaddr_num; vi++)
{
if (o.hash[vi] != hash[vi])
{
printf("Update (CPU -> GPU): type = %s, vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n",
Core::EnumName(o.type).C_Str(), h.vaddr[vi], h.size[vi]);
need_update = true;
o.hash[vi] = hash[vi];
}
}
if (need_update)
{
EXIT_IF(o.update_func == nullptr);
o.update_func(ctx, o.params, o.obj, vaddr, size, vaddr_num);
}
o.use_num++;
o.use_last_frame = m_current_frame;
o.in_use = true;
o.read_only = info.read_only;
o.check_hash = info.check_hash;
return o.obj;
}
}
if (h.overlaps_num == 1 &&
(h.overlaps[0].type == GpuMemoryObjectType::VideoOutBuffer && info.type == GpuMemoryObjectType::StorageBuffer))
{
update_object = &h;
break;
}
// EXIT("not implemented");
Free(ctx, h);
break;
}
for (int vi = 0; vi < vaddr_num; vi++)
{
EXIT_NOT_IMPLEMENTED(!h.free && vaddr_overlap(h.vaddr, h.size, h.overlaps_num, vaddr[vi], size[vi]));
}
}
for (int vi = 0; vi < vaddr_num; vi++)
{
EXIT_NOT_IMPLEMENTED(!IsAllocated(vaddr[vi], size[vi]));
}
ObjectInfo o {};
for (int i = 0; i < GpuObject::PARAMS_MAX; i++)
{
o.params[i] = info.params[i];
}
o.type = info.type;
o.obj = nullptr;
for (int vi = 0; vi < vaddr_num; vi++)
{
o.hash[vi] = hash[vi];
}
o.obj = info.Create(ctx, vaddr, size, vaddr_num, &o.mem);
o.write_back_func = info.GetWriteBackFunc();
o.delete_func = info.GetDeleteFunc();
o.update_func = info.GetUpdateFunc();
o.use_num = 1;
o.use_last_frame = m_current_frame;
o.in_use = true;
o.read_only = info.read_only;
o.check_hash = info.check_hash;
bool updated = false;
if (update_object != nullptr)
{
EXIT_IF(update_object->overlaps_num >= OBJ_OVERLAPS_MAX);
update_object->overlaps[update_object->overlaps_num++] = o;
updated = true;
} else
{
for (auto& u: m_objects)
{
if (u.free)
{
u.overlaps_num = 1;
u.overlaps[0] = o;
u.free = false;
for (int vi = 0; vi < vaddr_num; vi++)
{
u.vaddr[vi] = vaddr[vi];
u.size[vi] = size[vi];
m_objects_size += size[vi];
}
u.vaddr_num = vaddr_num;
updated = true;
break;
}
}
}
if (!updated)
{
Object h {};
for (int vi = 0; vi < vaddr_num; vi++)
{
h.vaddr[vi] = vaddr[vi];
h.size[vi] = size[vi];
m_objects_size += size[vi];
}
h.vaddr_num = vaddr_num;
h.overlaps_num = 1;
h.overlaps[0] = o;
h.free = false;
m_objects.Add(h);
}
return o.obj;
}
void GpuMemory::ResetHash(GraphicContext* /*ctx*/, uint64_t* vaddr, uint64_t* size, int vaddr_num, GpuMemoryObjectType type)
{
EXIT_IF(type == GpuMemoryObjectType::Invalid);
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num > VADDR_BLOCKS_MAX || vaddr_num <= 0);
Core::LockGuard lock(m_mutex);
uint64_t new_hash = 0;
for (auto& h: m_objects)
{
if (!h.free && vaddr_equal(h.vaddr, h.size, h.vaddr_num, vaddr, size, vaddr_num))
{
for (int oi = 0; oi < h.overlaps_num; oi++)
{
auto& o = h.overlaps[oi];
if (o.type == type)
{
for (int vi = 0; vi < h.vaddr_num; vi++)
{
printf("ResetHash: type = %s, vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64 ", old_hash = 0x%016" PRIx64
", new_hash = 0x%016" PRIx64 "\n",
Core::EnumName(o.type).C_Str(), h.vaddr[vi], h.size[vi], o.hash[vi], new_hash);
o.hash[vi] = new_hash;
}
}
}
}
}
}
void GpuMemory::Free(GraphicContext* ctx, uint64_t vaddr, uint64_t size)
{
Core::LockGuard lock(m_mutex);
printf("Release gpu objects:\n");
printf("\t gpu_vaddr = 0x%016" PRIx64 "\n", vaddr);
printf("\t size = 0x%016" PRIx64 "\n", size);
EXIT_NOT_IMPLEMENTED(!IsAllocated(vaddr, size));
int index = 0;
for (auto& a: m_allocated)
{
if (a.vaddr == vaddr && a.size == size)
{
m_allocated.RemoveAt(index);
break;
}
index++;
}
EXIT_NOT_IMPLEMENTED(IsAllocated(vaddr, size));
for (auto& h: m_objects)
{
for (int vi = 0; vi < h.vaddr_num; vi++)
{
if (!h.free && (h.vaddr[vi] >= vaddr && h.vaddr[vi] < vaddr + size))
{
Free(ctx, h);
break;
}
}
}
}
void GpuMemory::Free(GraphicContext* ctx, Object& h)
{
for (int oi = 0; oi < h.overlaps_num; oi++)
{
auto& o = h.overlaps[oi];
EXIT_IF(o.delete_func == nullptr);
if (o.delete_func != nullptr)
{
for (int vi = 0; vi < h.vaddr_num; vi++)
{
printf("Delete: type = %s, vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n", Core::EnumName(o.type).C_Str(),
h.vaddr[vi], h.size[vi]);
}
o.delete_func(ctx, o.obj, &o.mem);
}
}
h.overlaps_num = 0;
h.free = true;
for (int vi = 0; vi < h.vaddr_num; vi++)
{
m_objects_size -= h.size[vi];
}
h.vaddr_num = 0;
}
void GpuMemory::FrameDone()
{
Core::LockGuard lock(m_mutex);
m_current_frame++;
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
void GpuMemory::WriteBack(GraphicContext* ctx)
{
Core::LockGuard lock(m_mutex);
for (auto& h: m_objects)
{
if (!h.free)
{
for (int oi = 0; oi < h.overlaps_num; oi++)
{
auto& o = h.overlaps[oi];
if (o.in_use && /*o.use_last_frame >= m_current_frame &&*/ o.write_back_func != nullptr && !o.read_only)
{
o.write_back_func(ctx, o.obj, h.vaddr, h.size, h.vaddr_num);
for (int vi = 0; vi < h.vaddr_num; vi++)
{
uint64_t new_hash = 0;
if (o.check_hash)
{
new_hash = calc_hash(reinterpret_cast<const uint8_t*>(h.vaddr[vi]), h.size[vi]);
}
printf("WriteBack (GPU -> CPU): type = %s, vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64
", old_hash = 0x%016" PRIx64 ", new_hash = 0x%016" PRIx64 "\n",
Core::EnumName(o.type).C_Str(), h.vaddr[vi], h.size[vi], o.hash[vi], new_hash);
o.hash[vi] = new_hash;
}
for (int oi2 = 0; oi2 < h.overlaps_num; oi2++)
{
if (oi2 != oi)
{
auto& o2 = h.overlaps[oi2];
bool need_update = false;
for (int vi = 0; vi < h.vaddr_num; vi++)
{
uint64_t hash = o.hash[vi];
if (o2.hash[vi] != hash)
{
printf("Update (CPU -> GPU): type = %s, vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64
", old_hash = 0x%016" PRIx64 ", new_hash = 0x%016" PRIx64 "\n",
Core::EnumName(o2.type).C_Str(), h.vaddr[vi], h.size[vi], o2.hash[vi], hash);
o2.hash[vi] = hash;
need_update = true;
}
}
if (need_update)
{
EXIT_IF(o2.update_func == nullptr);
o2.update_func(ctx, o2.params, o2.obj, h.vaddr, h.size, h.vaddr_num);
}
}
}
o.in_use = false;
}
}
}
}
}
void GpuMemory::DbgDump()
{
Core::LockGuard lock(m_mutex);
printf("--- Gpu Memory ---\n");
for (auto& o: m_allocated)
{
printf("Allocated block: vaddr = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n", o.vaddr, o.size);
}
printf("m_current_frame = %" PRIu64 "\n", m_current_frame);
printf("m_objects_size = %" PRIu64 "\n", m_objects_size);
for (auto& h: m_objects)
{
if (!h.free)
{
printf("Object:\n");
for (int vi = 0; vi < h.vaddr_num; vi++)
{
printf("\t vaddr = 0x%016" PRIx64 "\n", h.vaddr[vi]);
printf("\t size = 0x%016" PRIx64 "\n", h.size[vi]);
GpuResources::Info res_info;
if (g_gpu_resources->FindInfo(h.vaddr[vi], &res_info))
{
printf("\t {\n");
printf("\t\t RegisteredResource: %s\n", res_info.name.C_Str());
printf("\t\t addr: %016" PRIx64 "\n", res_info.memory);
printf("\t\t size: %" PRIu64 "\n", res_info.size);
printf("\t\t type: %" PRIu32 "\n", res_info.type);
printf("\t\t user_data: %" PRIu64 "\n", res_info.user_data);
printf("\t }\n");
// EXIT_NOT_IMPLEMENTED(res_info.size != h.size[vi]);
// EXIT_NOT_IMPLEMENTED(res_info.memory != h.vaddr[vi]);
}
}
printf("\t overlaps_num = %d\n", h.overlaps_num);
for (int oi = 0; oi < h.overlaps_num; oi++)
{
auto& o = h.overlaps[oi];
printf("\t [%d] type = %s\n", oi, Core::EnumName(o.type).C_Str());
for (int vi = 0; vi < h.vaddr_num; vi++)
{
printf("\t [%d] hash = 0x%016" PRIx64 "\n", oi, o.hash[vi]);
}
printf("\t [%d] vk_size = 0x%016" PRIx64 "\n", oi, o.mem.requirements.size);
printf("\t [%d] vk_align = 0x%016" PRIx64 "\n", oi, o.mem.requirements.alignment);
printf("\t [%d] vk_type = 0x%08" PRIx32 "\n", oi, o.mem.type);
printf("\t [%d] use_last_frame = %" PRIu64 "\n", oi, o.use_last_frame);
printf("\t [%d] use_num = %" PRIu64 "\n", oi, o.use_num);
printf("\t [%d] in_use = %s\n", oi, o.in_use ? "true" : "false");
printf("\t [%d] read_only = %s\n", oi, o.read_only ? "true" : "false");
printf("\t [%d] check_hash = %s\n", oi, o.check_hash ? "true" : "false");
}
}
}
}
void GpuMemoryInit()
{
EXIT_IF(g_gpu_memory != nullptr);
EXIT_IF(g_gpu_resources != nullptr);
g_gpu_memory = new GpuMemory;
g_gpu_resources = new GpuResources;
}
void GpuMemorySetAllocatedRange(uint64_t vaddr, uint64_t size)
{
EXIT_IF(g_gpu_memory == nullptr);
g_gpu_memory->SetAllocatedRange(vaddr, size);
}
void GpuMemoryFree(GraphicContext* ctx, uint64_t vaddr, uint64_t size)
{
EXIT_IF(g_gpu_memory == nullptr);
EXIT_IF(ctx == nullptr);
g_gpu_memory->Free(ctx, vaddr, size);
}
void* GpuMemoryGetObject(GraphicContext* ctx, uint64_t vaddr, uint64_t size, const GpuObject& info)
{
EXIT_IF(g_gpu_memory == nullptr);
EXIT_IF(ctx == nullptr);
return g_gpu_memory->GetObject(ctx, &vaddr, &size, 1, info);
}
void* GpuMemoryGetObject(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, const GpuObject& info)
{
EXIT_IF(g_gpu_memory == nullptr);
EXIT_IF(ctx == nullptr);
return g_gpu_memory->GetObject(ctx, vaddr, size, vaddr_num, info);
}
void GpuMemoryResetHash(GraphicContext* ctx, uint64_t vaddr, uint64_t size, GpuMemoryObjectType type)
{
EXIT_IF(g_gpu_memory == nullptr);
EXIT_IF(ctx == nullptr);
g_gpu_memory->ResetHash(ctx, &vaddr, &size, 1, type);
}
void GpuMemoryDbgDump()
{
EXIT_IF(g_gpu_memory == nullptr);
g_gpu_memory->DbgDump();
}
void GpuMemoryFlush()
{
EXIT_IF(g_gpu_memory == nullptr);
// TODO(): update vulkan objects after CPU-drawing
}
void GpuMemoryFrameDone()
{
EXIT_IF(g_gpu_memory == nullptr);
g_gpu_memory->FrameDone();
}
void GpuMemoryWriteBack(GraphicContext* ctx)
{
EXIT_IF(g_gpu_memory == nullptr);
EXIT_IF(ctx == nullptr);
g_gpu_memory->WriteBack(ctx);
}
bool VulkanAllocate(GraphicContext* ctx, VulkanMemory* mem)
{
static std::atomic<uint64_t> seq = 0;
EXIT_IF(ctx == nullptr);
EXIT_IF(mem == nullptr);
EXIT_IF(mem->memory != nullptr);
EXIT_IF(mem->requirements.size == 0);
VkPhysicalDeviceMemoryProperties memory_properties {};
vkGetPhysicalDeviceMemoryProperties(ctx->physical_device, &memory_properties);
uint32_t index = 0;
for (; index < memory_properties.memoryTypeCount; index++)
{
if ((mem->requirements.memoryTypeBits & (static_cast<uint32_t>(1) << index)) != 0 &&
(memory_properties.memoryTypes[index].propertyFlags & mem->property) == mem->property)
{
break;
}
}
mem->type = index;
mem->offset = 0;
VkMemoryAllocateInfo alloc_info {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.pNext = nullptr;
alloc_info.allocationSize = mem->requirements.size;
alloc_info.memoryTypeIndex = index;
mem->unique_id = ++seq;
return (vkAllocateMemory(ctx->device, &alloc_info, nullptr, &mem->memory) == VK_SUCCESS);
}
void VulkanFree(GraphicContext* ctx, VulkanMemory* mem)
{
EXIT_IF(ctx == nullptr);
EXIT_IF(mem == nullptr);
vkFreeMemory(ctx->device, mem->memory, nullptr);
mem->memory = nullptr;
}
void VulkanMapMemory(GraphicContext* ctx, VulkanMemory* mem, void** data)
{
EXIT_IF(ctx == nullptr);
EXIT_IF(mem == nullptr);
EXIT_IF(data == nullptr);
vkMapMemory(ctx->device, mem->memory, mem->offset, mem->requirements.size, 0, data);
}
void VulkanUnmapMemory(GraphicContext* ctx, VulkanMemory* mem)
{
EXIT_IF(ctx == nullptr);
EXIT_IF(mem == nullptr);
vkUnmapMemory(ctx->device, mem->memory);
}
void VulkanBindImageMemory(GraphicContext* ctx, TextureVulkanImage* image, VulkanMemory* mem)
{
EXIT_IF(ctx == nullptr);
EXIT_IF(mem == nullptr);
EXIT_IF(image == nullptr);
vkBindImageMemory(ctx->device, image->image, mem->memory, mem->offset);
}
void VulkanBindImageMemory(GraphicContext* ctx, VideoOutVulkanImage* image, VulkanMemory* mem)
{
EXIT_IF(ctx == nullptr);
EXIT_IF(mem == nullptr);
EXIT_IF(image == nullptr);
vkBindImageMemory(ctx->device, image->image, mem->memory, mem->offset);
}
void VulkanBindImageMemory(GraphicContext* ctx, DepthStencilVulkanImage* image, VulkanMemory* mem)
{
EXIT_IF(ctx == nullptr);
EXIT_IF(mem == nullptr);
EXIT_IF(image == nullptr);
vkBindImageMemory(ctx->device, image->image, mem->memory, mem->offset);
}
void VulkanBindBufferMemory(GraphicContext* ctx, VulkanBuffer* buffer, VulkanMemory* mem)
{
EXIT_IF(ctx == nullptr);
EXIT_IF(mem == nullptr);
EXIT_IF(buffer == nullptr);
vkBindBufferMemory(ctx->device, buffer->buffer, mem->memory, mem->offset);
}
void GpuMemoryRegisterOwner(uint32_t* owner_handle, const char* name)
{
EXIT_IF(g_gpu_resources == nullptr);
EXIT_IF(owner_handle == nullptr);
EXIT_IF(name == nullptr);
*owner_handle = g_gpu_resources->AddOwner(String::FromUtf8(name));
}
void GpuMemoryRegisterResource(uint32_t* resource_handle, uint32_t owner_handle, const void* memory, size_t size, const char* name,
uint32_t type, uint64_t user_data)
{
EXIT_IF(g_gpu_resources == nullptr);
EXIT_IF(resource_handle == nullptr);
EXIT_IF(name == nullptr);
*resource_handle =
g_gpu_resources->AddResource(owner_handle, reinterpret_cast<uint64_t>(memory), size, String::FromUtf8(name), type, user_data);
}
void GpuMemoryUnregisterAllResourcesForOwner(uint32_t owner_handle)
{
EXIT_IF(g_gpu_resources == nullptr);
g_gpu_resources->DeleteResources(owner_handle);
}
void GpuMemoryUnregisterOwnerAndResources(uint32_t owner_handle)
{
EXIT_IF(g_gpu_resources == nullptr);
g_gpu_resources->DeleteOwner(owner_handle);
}
void GpuMemoryUnregisterResource(uint32_t resource_handle)
{
EXIT_IF(g_gpu_resources == nullptr);
g_gpu_resources->DeleteResource(resource_handle);
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED
+544
View File
@@ -0,0 +1,544 @@
#include "Emulator/Graphics/Graphics.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/File.h"
#include "Kyty/Core/String.h"
#include "Emulator/Config.h"
#include "Emulator/Graphics/GpuMemory.h"
#include "Emulator/Graphics/GraphicsRender.h"
#include "Emulator/Graphics/GraphicsRun.h"
#include "Emulator/Graphics/HardwareContext.h"
#include "Emulator/Graphics/Label.h"
#include "Emulator/Graphics/Pm4.h"
#include "Emulator/Graphics/Tile.h"
#include "Emulator/Graphics/VideoOut.h"
#include "Emulator/Graphics/Window.h"
#include "Emulator/Kernel/Pthread.h"
#include "Emulator/Libs/Errno.h"
#include "Emulator/Libs/Libs.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
LIB_NAME("GraphicsDriver", "GraphicsDriver");
KYTY_SUBSYSTEM_INIT(Graphics)
{
auto width = Config::GetScreenWidth();
auto height = Config::GetScreenHeight();
WindowInit(width, height);
VideoOut::VideoOutInit(width, height);
GraphicsRenderInit();
GraphicsRunInit();
GpuMemoryInit();
LabelInit();
TileInit();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Graphics) {}
KYTY_SUBSYSTEM_DESTROY(Graphics) {}
int KYTY_SYSV_ABI GraphicsSetVsShader(uint32_t* cmd, uint64_t size, const VsStageRegisters* vs_regs, uint32_t shader_modifier)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < sizeof(VsStageRegisters) / 4 + 2);
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\t size = %" PRIu64 "\n", size);
printf("\t shader_modifier = %" PRIu32 "\n", shader_modifier);
printf("\t vs_regs.m_spiShaderPgmLoVs = %08" PRIx32 "\n", vs_regs->m_spiShaderPgmLoVs);
printf("\t vs_regs.m_spiShaderPgmHiVs = %08" PRIx32 "\n", vs_regs->m_spiShaderPgmHiVs);
printf("\t vs_regs.m_spiShaderPgmRsrc1Vs = %08" PRIx32 "\n", vs_regs->m_spiShaderPgmRsrc1Vs);
printf("\t vs_regs.m_spiShaderPgmRsrc2Vs = %08" PRIx32 "\n", vs_regs->m_spiShaderPgmRsrc2Vs);
printf("\t vs_regs.m_spiVsOutConfig = %08" PRIx32 "\n", vs_regs->m_spiVsOutConfig);
printf("\t vs_regs.m_spiShaderPosFormat = %08" PRIx32 "\n", vs_regs->m_spiShaderPosFormat);
printf("\t vs_regs.m_paClVsOutCntl = %08" PRIx32 "\n", vs_regs->m_paClVsOutCntl);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_VS);
cmd[1] = shader_modifier;
memcpy(&cmd[2], vs_regs, sizeof(VsStageRegisters));
return OK;
}
int KYTY_SYSV_ABI GraphicsSetEmbeddedVsShader(uint32_t* cmd, uint64_t size, uint32_t id, uint32_t shader_modifier)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < 3);
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\t size = %" PRIu64 "\n", size);
printf("\t id = %" PRIu32 "\n", id);
printf("\t shader_modifier = %" PRIu32 "\n", shader_modifier);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_VS_EMBEDDED);
cmd[1] = shader_modifier;
cmd[2] = id;
return OK;
}
int KYTY_SYSV_ABI GraphicsSetPsShader350(uint32_t* cmd, uint64_t size, const uint32_t* ps_regs)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < sizeof(PsStageRegisters) / 12 + 1);
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\t size = %" PRIu64 "\n", size);
printf("\t ps_regs.m_spiShaderPgmLoPs = %08" PRIx32 "\n", ps_regs[0]);
printf("\t ps_regs.m_spiShaderPgmHiPs = %08" PRIx32 "\n", ps_regs[1]);
printf("\t ps_regs.m_spiShaderPgmRsrc1Ps = %08" PRIx32 "\n", ps_regs[2]);
printf("\t ps_regs.m_spiShaderPgmRsrc2Ps = %08" PRIx32 "\n", ps_regs[3]);
printf("\t ps_regs.m_spiShaderZFormat = %08" PRIx32 "\n", ps_regs[4]);
printf("\t ps_regs.m_spiShaderColFormat = %08" PRIx32 "\n", ps_regs[5]);
printf("\t ps_regs.m_spiPsInputEna = %08" PRIx32 "\n", ps_regs[6]);
printf("\t ps_regs.m_spiPsInputAddr = %08" PRIx32 "\n", ps_regs[7]);
printf("\t ps_regs.m_spiPsInControl = %08" PRIx32 "\n", ps_regs[8]);
printf("\t ps_regs.m_spiBarycCntl = %08" PRIx32 "\n", ps_regs[9]);
printf("\t ps_regs.m_dbShaderControl = %08" PRIx32 "\n", ps_regs[10]);
printf("\t ps_regs.m_cbShaderMask = %08" PRIx32 "\n", ps_regs[11]);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_PS);
memcpy(&cmd[1], ps_regs, 12 * 4);
return OK;
}
int KYTY_SYSV_ABI GraphicsSetCsShaderWithModifier(uint32_t* cmd, uint64_t size, const uint32_t* cs_regs, uint32_t shader_modifier)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < 7 + 2);
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\t size = %" PRIu64 "\n", size);
printf("\t shader_modifier = %" PRIu32 "\n", shader_modifier);
printf("\t cs_regs.m_computePgmLo = %08" PRIx32 "\n", cs_regs[0]);
printf("\t cs_regs.m_computePgmHi = %08" PRIx32 "\n", cs_regs[1]);
printf("\t cs_regs.m_computePgmRsrc1 = %08" PRIx32 "\n", cs_regs[2]);
printf("\t cs_regs.m_computePgmRsrc2 = %08" PRIx32 "\n", cs_regs[3]);
printf("\t cs_regs.m_computeNumThreadX = %08" PRIx32 "\n", cs_regs[4]);
printf("\t cs_regs.m_computeNumThreadY = %08" PRIx32 "\n", cs_regs[5]);
printf("\t cs_regs.m_computeNumThreadZ = %08" PRIx32 "\n", cs_regs[6]);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_CS);
cmd[1] = shader_modifier;
memcpy(&cmd[2], cs_regs, 7 * 4);
return OK;
}
int KYTY_SYSV_ABI GraphicsDrawIndex(uint32_t* cmd, uint64_t size, uint32_t index_count, const void* index_addr, uint32_t flags,
uint32_t type)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < 6);
printf("\tcmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\tsize = %" PRIu64 "\n", size);
printf("\tindex_count = %" PRIu32 "\n", index_count);
printf("\tindex_addr = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(index_addr));
printf("\tflags = %08" PRIx32 "\n", flags);
printf("\ttype = %" PRIu32 "\n", type);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_DRAW_INDEX);
cmd[1] = index_count;
cmd[2] = static_cast<uint32_t>(reinterpret_cast<uint64_t>(index_addr) & 0xffffffffu);
cmd[3] = static_cast<uint32_t>((reinterpret_cast<uint64_t>(index_addr) >> 32u) & 0xffffffffu);
cmd[4] = flags;
cmd[5] = type;
return OK;
}
int KYTY_SYSV_ABI GraphicsDrawIndexAuto(uint32_t* cmd, uint64_t size, uint32_t index_count, uint32_t flags)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < 3);
printf("\tcmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\tsize = %" PRIu64 "\n", size);
printf("\tindex_count = %" PRIu32 "\n", index_count);
printf("\tflags = %08" PRIx32 "\n", flags);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_DRAW_INDEX_AUTO);
cmd[1] = index_count;
cmd[2] = flags;
return OK;
}
int KYTY_SYSV_ABI GraphicsInsertWaitFlipDone(uint32_t* cmd, uint64_t size, uint32_t video_out_handle, uint32_t display_buffer_index)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < 3);
printf("\tcmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\tsize = %" PRIu64 "\n", size);
printf("\tvideo_out_handle = %" PRIu32 "\n", video_out_handle);
printf("\tdisplay_buffer_index = %" PRIu32 "\n", display_buffer_index);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_WAIT_FLIP_DONE);
cmd[1] = video_out_handle;
cmd[2] = display_buffer_index;
return OK;
}
int KYTY_SYSV_ABI GraphicsDispatchDirect(uint32_t* cmd, uint64_t size, uint32_t thread_group_x, uint32_t thread_group_y,
uint32_t thread_group_z, uint32_t mode)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < 5);
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\t size = %" PRIu64 "\n", size);
printf("\t thread_group_x = %" PRIu32 "\n", thread_group_x);
printf("\t thread_group_y = %" PRIu32 "\n", thread_group_y);
printf("\t thread_group_z = %" PRIu32 "\n", thread_group_z);
printf("\t mode = %" PRIu32 "\n", mode);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_DISPATCH_DIRECT);
cmd[1] = thread_group_x;
cmd[2] = thread_group_y;
cmd[3] = thread_group_z;
cmd[4] = mode;
return OK;
}
uint32_t KYTY_SYSV_ABI GraphicsDrawInitDefaultHardwareState350(uint32_t* cmd, uint64_t size)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < 2);
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\t size = %" PRIu64 "\n", size);
cmd[0] = KYTY_PM4(2, Pm4::IT_NOP, Pm4::R_DRAW_RESET);
return 2;
}
uint32_t KYTY_SYSV_ABI GraphicsDispatchInitDefaultHardwareState(uint32_t* cmd, uint64_t size)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < 2);
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\t size = %" PRIu64 "\n", size);
cmd[0] = KYTY_PM4(2, Pm4::IT_NOP, Pm4::R_DISPATCH_RESET);
return 2;
}
static void dbg_dump_dcb(const char* type, uint32_t num_dw, uint32_t* cmd_buffer)
{
EXIT_IF(type == nullptr);
static int id = 0;
if (Config::CommandBufferDumpEnabled() && num_dw > 0 && cmd_buffer != nullptr)
{
Core::File f;
String file_name = Config::GetCommandBufferDumpFolder().FixDirectorySlash() +
String::FromPrintf("%04d_%04d_buffer_%s.log", GraphicsRunGetFrameNum(), id++, type);
Core::File::CreateDirectories(file_name.DirectoryWithoutFilename());
f.Create(file_name);
if (f.IsInvalid())
{
printf(FG_BRIGHT_RED "Can't create file: %s\n" FG_DEFAULT, file_name.C_Str());
return;
}
Pm4::DumpPm4PacketStream(&f, cmd_buffer, 0, num_dw);
f.Close();
}
}
int KYTY_SYSV_ABI GraphicsSubmitCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes,
void* ccb_gpu_addrs[], const uint32_t* ccb_sizes_in_bytes)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(count != 1);
auto* dcb = (dcb_gpu_addrs == nullptr ? nullptr : static_cast<uint32_t*>(dcb_gpu_addrs[0]));
auto dcb_size = (dcb_sizes_in_bytes == nullptr ? 0 : dcb_sizes_in_bytes[0] / 4);
auto* ccb = (ccb_gpu_addrs == nullptr ? nullptr : static_cast<uint32_t*>(ccb_gpu_addrs[0]));
auto ccb_size = (ccb_sizes_in_bytes == nullptr ? 0 : ccb_sizes_in_bytes[0] / 4);
dbg_dump_dcb("d", dcb_size, dcb);
dbg_dump_dcb("c", ccb_size, ccb);
GraphicsRunSubmit(dcb, dcb_size, ccb, ccb_size);
return OK;
}
int KYTY_SYSV_ABI GraphicsSubmitAndFlipCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes,
void* ccb_gpu_addrs[], const uint32_t* ccb_sizes_in_bytes, int handle, int index,
int flip_mode, int64_t flip_arg)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(count != 1);
auto* dcb = (dcb_gpu_addrs == nullptr ? nullptr : static_cast<uint32_t*>(dcb_gpu_addrs[0]));
auto dcb_size = (dcb_sizes_in_bytes == nullptr ? 0 : dcb_sizes_in_bytes[0] / 4);
auto* ccb = (ccb_gpu_addrs == nullptr ? nullptr : static_cast<uint32_t*>(ccb_gpu_addrs[0]));
auto ccb_size = (ccb_sizes_in_bytes == nullptr ? 0 : ccb_sizes_in_bytes[0] / 4);
dbg_dump_dcb("d", dcb_size, dcb);
dbg_dump_dcb("c", ccb_size, ccb);
printf("\t handle = %" PRId32 "\n", handle);
printf("\t index = %" PRId32 "\n", index);
printf("\t flip_mode = %" PRId32 "\n", flip_mode);
printf("\t flip_arg = %" PRId64 "\n", flip_arg);
GraphicsRunSubmitAndFlip(dcb, dcb_size, ccb, ccb_size, handle, index, flip_mode, flip_arg);
return OK;
}
int KYTY_SYSV_ABI GraphicsSubmitDone()
{
PRINT_NAME();
GraphicsRunDone();
// GpuMemoryFrameDone();
// GpuMemoryDbgDump();
return OK;
}
void KYTY_SYSV_ABI GraphicsFlushMemory()
{
PRINT_NAME();
GraphicsRunDone();
EXIT("1");
}
int KYTY_SYSV_ABI GraphicsAddEqEvent(LibKernel::EventQueue::KernelEqueue eq, int id, void* udata)
{
PRINT_NAME();
if (eq == nullptr)
{
return LibKernel::KERNEL_ERROR_EBADF;
}
return GraphicsRenderAddEqEvent(eq, id, udata);
}
int KYTY_SYSV_ABI GraphicsDeleteEqEvent(LibKernel::EventQueue::KernelEqueue eq, int id)
{
PRINT_NAME();
if (eq == nullptr)
{
return LibKernel::KERNEL_ERROR_EBADF;
}
return GraphicsRenderDeleteEqEvent(eq, id);
}
uint32_t KYTY_SYSV_ABI GraphicsMapComputeQueue(uint32_t pipe_id, uint32_t queue_id, uint32_t* ring_addr, uint32_t ring_size_dw,
uint32_t* read_ptr_addr)
{
PRINT_NAME();
printf("\t pipe_id = %" PRIu32 "\n", pipe_id);
printf("\t queue_id = %" PRIu32 "\n", queue_id);
printf("\t ring_addr = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(ring_addr));
printf("\t ring_size_dw = %" PRIu32 "\n", ring_size_dw);
printf("\t read_ptr_addr = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(read_ptr_addr));
uint32_t id = GraphicsRunMapComputeQueue(pipe_id, queue_id, ring_addr, ring_size_dw, read_ptr_addr);
printf("\t queue = %" PRIu32 "\n", id);
return id;
}
void KYTY_SYSV_ABI GraphicsUnmapComputeQueue(uint32_t id)
{
PRINT_NAME();
printf("\t id = %" PRIu32 "\n", id);
GraphicsRunUnmapComputeQueue(id);
}
int KYTY_SYSV_ABI GraphicsComputeWaitOnAddress(uint32_t* cmd, uint64_t size, uint32_t* gpu_addr, uint32_t mask, uint32_t func, uint32_t ref)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < 6);
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\t size = %" PRIu64 "\n", size);
printf("\t gpu_addr = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(gpu_addr));
printf("\t mask = %08" PRIx32 "\n", mask);
printf("\t func = %" PRIu32 "\n", func);
printf("\t ref = %08" PRIx32 "\n", ref);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_DISPATCH_WAIT_MEM);
cmd[1] = static_cast<uint32_t>(reinterpret_cast<uint64_t>(gpu_addr) & 0xffffffffu);
cmd[2] = static_cast<uint32_t>((reinterpret_cast<uint64_t>(gpu_addr) >> 32u) & 0xffffffffu);
cmd[3] = mask;
cmd[4] = func;
cmd[5] = ref;
return OK;
}
void KYTY_SYSV_ABI GraphicsDingDong(uint32_t ring_id, uint32_t offset_dw)
{
PRINT_NAME();
printf("\t ring_id = %" PRIu32 "\n", ring_id);
printf("\t offset_dw = %" PRIu32 "\n", offset_dw);
GraphicsRunDingDong(ring_id, offset_dw);
}
int KYTY_SYSV_ABI GraphicsInsertPushMarker(uint32_t* cmd, uint64_t size, const char* str)
{
PRINT_NAME();
auto len = strlen(str) + 1;
EXIT_NOT_IMPLEMENTED(size * 4 < len + 1);
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\t size = %" PRIu64 "\n", size);
printf("\t str = %s\n", str);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_PUSH_MARKER);
memcpy(cmd + 1, str, len);
return OK;
}
int KYTY_SYSV_ABI GraphicsInsertPopMarker(uint32_t* cmd, uint64_t size)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(size < 2);
printf("\t cmd_buffer = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(cmd));
printf("\t size = %" PRIu64 "\n", size);
cmd[0] = KYTY_PM4(size, Pm4::IT_NOP, Pm4::R_POP_MARKER);
return OK;
}
uint64_t KYTY_SYSV_ABI GraphicsGetGpuCoreClockFrequency()
{
return LibKernel::KernelGetTscFrequency();
}
int KYTY_SYSV_ABI GraphicsRegisterOwner(uint32_t* owner_handle, const char* name)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(owner_handle == nullptr);
EXIT_NOT_IMPLEMENTED(name == nullptr);
printf("\t RegisterOwner: %s\n", name);
GpuMemoryRegisterOwner(owner_handle, name);
printf("\t handler: %" PRIu32 "\n", *owner_handle);
return OK;
}
int KYTY_SYSV_ABI GraphicsRegisterResource(uint32_t* resource_handle, uint32_t owner_handle, const void* memory, size_t size,
const char* name, uint32_t type, uint64_t user_data)
{
PRINT_NAME();
// EXIT_NOT_IMPLEMENTED(resource_handle == nullptr);
EXIT_NOT_IMPLEMENTED(memory == nullptr);
EXIT_NOT_IMPLEMENTED(name == nullptr);
printf("\t RegisterResource: %s\n", name);
printf("\t owner_handle: %" PRIu32 "\n", owner_handle);
printf("\t addr: %016" PRIx64 "\n", reinterpret_cast<uint64_t>(memory));
printf("\t size: %" PRIu64 "\n", size);
printf("\t type: %" PRIu32 "\n", type);
printf("\t user_data: %" PRIu64 "\n", user_data);
uint32_t rhandle = 0;
GpuMemoryRegisterResource(&rhandle, owner_handle, memory, size, name, type, user_data);
printf("\t handler: %" PRIu32 "\n", rhandle);
if (resource_handle != nullptr)
{
*resource_handle = rhandle;
}
return OK;
}
int KYTY_SYSV_ABI GraphicsUnregisterAllResourcesForOwner(uint32_t owner_handle)
{
PRINT_NAME();
printf("\t owner_handle: %" PRIu32 "\n", owner_handle);
GpuMemoryUnregisterAllResourcesForOwner(owner_handle);
return OK;
}
int KYTY_SYSV_ABI GraphicsUnregisterOwnerAndResources(uint32_t owner_handle)
{
PRINT_NAME();
printf("\t owner_handle: %" PRIu32 "\n", owner_handle);
GpuMemoryUnregisterOwnerAndResources(owner_handle);
return OK;
}
int KYTY_SYSV_ABI GraphicsUnregisterResource(uint32_t resource_handle)
{
PRINT_NAME();
printf("\t resource_handle: %" PRIu32 "\n", resource_handle);
GpuMemoryUnregisterResource(resource_handle);
return OK;
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,94 @@
#include "Emulator/Graphics/IndexBuffer.h"
#include "Kyty/Core/DbgAssert.h"
#include "Emulator/Graphics/GraphicContext.h"
#include "Emulator/Graphics/Utils.h"
#include "Emulator/Profiler.h"
#include <vulkan/vulkan_core.h>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
void* IndexBufferGpuObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const
{
KYTY_PROFILER_BLOCK("IndexBufferGpuObject::Create");
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr || *vaddr == 0);
EXIT_IF(mem == nullptr);
EXIT_IF(ctx == nullptr);
auto* vk_obj = new VulkanBuffer;
vk_obj->usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
vk_obj->memory.property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
vk_obj->buffer = nullptr;
VulkanCreateBuffer(ctx, *size, vk_obj);
EXIT_NOT_IMPLEMENTED(vk_obj->buffer == nullptr);
VulkanBuffer staging_buffer {};
staging_buffer.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
staging_buffer.memory.property = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
VulkanCreateBuffer(ctx, *size, &staging_buffer);
EXIT_NOT_IMPLEMENTED(staging_buffer.buffer == nullptr);
void* data = nullptr;
// vkMapMemory(ctx->device, staging_buffer.memory.memory, staging_buffer.memory.offset, *size, 0, &data);
VulkanMapMemory(ctx, &staging_buffer.memory, &data);
memcpy(data, reinterpret_cast<void*>(*vaddr), *size);
// vkUnmapMemory(ctx->device, staging_buffer.memory.memory);
VulkanUnmapMemory(ctx, &staging_buffer.memory);
UtilCopyBuffer(&staging_buffer, vk_obj, *size);
VulkanDeleteBuffer(ctx, &staging_buffer);
return vk_obj;
}
static void update_func(GraphicContext* /*ctx*/, const uint64_t* /*params*/, void* /*obj*/, const uint64_t* /*vaddr*/,
const uint64_t* /*size*/, int /*vaddr_num*/)
{
KYTY_PROFILER_BLOCK("IndexBufferGpuObject::update_func");
KYTY_NOT_IMPLEMENTED;
}
bool IndexBufferGpuObject::Equal(const uint64_t* /*other*/) const
{
return true;
}
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* /*mem*/)
{
KYTY_PROFILER_BLOCK("IndexBufferGpuObject::delete_func");
auto* vk_obj = reinterpret_cast<VulkanBuffer*>(obj);
EXIT_IF(vk_obj == nullptr);
EXIT_IF(vk_obj->buffer == nullptr);
EXIT_IF(ctx == nullptr);
VulkanDeleteBuffer(ctx, vk_obj);
delete vk_obj;
}
GpuObject::delete_func_t IndexBufferGpuObject::GetDeleteFunc() const
{
return delete_func;
}
GpuObject::update_func_t IndexBufferGpuObject::GetUpdateFunc() const
{
return update_func;
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED
+349
View File
@@ -0,0 +1,349 @@
#include "Emulator/Graphics/Label.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Threads.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Graphics/GraphicContext.h"
#include "Emulator/Graphics/GraphicsRender.h"
#include "Emulator/Profiler.h"
#include <vulkan/vulkan_core.h>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
struct Label
{
VkDevice device = nullptr;
VkEvent event = nullptr;
bool active = false;
uint64_t* dst_gpu_addr64 = nullptr;
uint64_t value64 = 0;
uint32_t* dst_gpu_addr32 = nullptr;
uint32_t value32 = 0;
LabelGpuObject::callback_t callback_1 = nullptr;
LabelGpuObject::callback_t callback_2 = nullptr;
uint64_t args[4] = {};
};
class LabelManager
{
public:
LabelManager()
{
EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
Core::Thread t(ThreadRun, this);
t.Detach();
}
virtual ~LabelManager() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(LabelManager);
Label* Create(GraphicContext* ctx, uint64_t* dst_gpu_addr, uint64_t value, LabelGpuObject::callback_t callback_1,
LabelGpuObject::callback_t callback_2, const uint64_t* args);
Label* Create(GraphicContext* ctx, uint32_t* dst_gpu_addr, uint32_t value, LabelGpuObject::callback_t callback_1,
LabelGpuObject::callback_t callback_2, const uint64_t* args);
void Delete(Label* label);
void Set(CommandBuffer* buffer, Label* label);
private:
static void ThreadRun(void* data);
Core::Mutex m_mutex;
Core::CondVar m_cond_var;
Vector<Label*> m_labels;
};
static LabelManager* g_label_manager = nullptr;
void LabelManager::ThreadRun(void* data)
{
auto* manager = static_cast<LabelManager*>(data);
for (;;)
{
manager->m_mutex.Lock();
int active_count = 0;
for (auto& label: manager->m_labels)
{
if (label->active)
{
active_count++;
if (vkGetEventStatus(label->device, label->event) == VK_EVENT_SET)
{
label->active = false;
bool write = true;
if (label->callback_1 != nullptr)
{
write = label->callback_1(label->args);
}
if (write && label->dst_gpu_addr64 != nullptr)
{
*label->dst_gpu_addr64 = label->value64;
printf(FG_BRIGHT_GREEN "EndOfPipe Signal!!! [0x%016" PRIx64 "] <- 0x%016" PRIx64 "\n" FG_DEFAULT,
reinterpret_cast<uint64_t>(label->dst_gpu_addr64), label->value64);
}
if (write && label->dst_gpu_addr32 != nullptr)
{
*label->dst_gpu_addr32 = label->value32;
printf(FG_BRIGHT_GREEN "EndOfPipe Signal!!! [0x%016" PRIx64 "] <- 0x%08" PRIx32 "\n" FG_DEFAULT,
reinterpret_cast<uint64_t>(label->dst_gpu_addr32), label->value32);
}
if (label->callback_2 != nullptr)
{
label->callback_2(label->args);
}
}
}
}
if (active_count == 0)
{
manager->m_cond_var.Wait(&manager->m_mutex);
}
manager->m_mutex.Unlock();
Core::Thread::SleepMicro(100);
}
}
Label* LabelManager::Create(GraphicContext* ctx, uint64_t* dst_gpu_addr, uint64_t value, LabelGpuObject::callback_t callback_1,
LabelGpuObject::callback_t callback_2, const uint64_t* args)
{
EXIT_IF(ctx == nullptr);
EXIT_IF(dst_gpu_addr == nullptr);
Core::LockGuard lock(m_mutex);
auto* label = new Label;
label->active = false;
label->dst_gpu_addr64 = dst_gpu_addr;
label->value64 = value;
label->dst_gpu_addr32 = nullptr;
label->value32 = 0;
label->event = nullptr;
label->device = ctx->device;
label->callback_1 = callback_1;
label->callback_2 = callback_2;
label->args[0] = args[0];
label->args[1] = args[1];
label->args[2] = args[2];
label->args[3] = args[3];
VkEventCreateInfo create_info {};
create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
create_info.pNext = nullptr;
create_info.flags = 0;
vkCreateEvent(ctx->device, &create_info, nullptr, &label->event);
EXIT_NOT_IMPLEMENTED(label->event == nullptr);
m_labels.Add(label);
return label;
}
Label* LabelManager::Create(GraphicContext* ctx, uint32_t* dst_gpu_addr, uint32_t value, LabelGpuObject::callback_t callback_1,
LabelGpuObject::callback_t callback_2, const uint64_t* args)
{
EXIT_IF(ctx == nullptr);
EXIT_IF(dst_gpu_addr == nullptr);
EXIT_IF(args == nullptr);
Core::LockGuard lock(m_mutex);
auto* label = new Label;
label->active = false;
label->dst_gpu_addr32 = dst_gpu_addr;
label->value32 = value;
label->dst_gpu_addr64 = nullptr;
label->value64 = 0;
label->event = nullptr;
label->device = ctx->device;
label->callback_1 = callback_1;
label->callback_2 = callback_2;
label->args[0] = args[0];
label->args[1] = args[1];
label->args[2] = args[2];
label->args[3] = args[3];
VkEventCreateInfo create_info {};
create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
create_info.pNext = nullptr;
create_info.flags = 0;
vkCreateEvent(ctx->device, &create_info, nullptr, &label->event);
EXIT_NOT_IMPLEMENTED(label->event == nullptr);
m_labels.Add(label);
return label;
}
void LabelManager::Delete(Label* label)
{
EXIT_IF(label == nullptr);
EXIT_IF(label->event == nullptr);
EXIT_IF(label->device == nullptr);
Core::LockGuard lock(m_mutex);
auto index = m_labels.Find(label);
EXIT_NOT_IMPLEMENTED(!m_labels.IndexValid(index));
m_labels.RemoveAt(index);
EXIT_NOT_IMPLEMENTED(label->active);
vkDestroyEvent(label->device, label->event, nullptr);
delete label;
}
void LabelManager::Set(CommandBuffer* buffer, Label* label)
{
EXIT_IF(label == nullptr);
EXIT_IF(buffer == nullptr);
EXIT_IF(buffer->IsInvalid());
EXIT_IF(label->event == nullptr);
EXIT_IF(label->device == nullptr);
Core::LockGuard lock(m_mutex);
auto index = m_labels.Find(label);
EXIT_NOT_IMPLEMENTED(!m_labels.IndexValid(index));
EXIT_NOT_IMPLEMENTED(label->active);
label->active = true;
EXIT_IF(label->event == nullptr);
auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()];
EXIT_NOT_IMPLEMENTED(vk_buffer == nullptr);
vkResetEvent(label->device, label->event);
vkCmdSetEvent(vk_buffer, label->event, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
m_cond_var.Signal();
}
void LabelInit()
{
EXIT_IF(g_label_manager != nullptr);
g_label_manager = new LabelManager;
}
Label* LabelCreate(GraphicContext* ctx, uint64_t* dst_gpu_addr, uint64_t value, LabelGpuObject::callback_t callback_1,
LabelGpuObject::callback_t callback_2, const uint64_t* args)
{
EXIT_IF(g_label_manager == nullptr);
return g_label_manager->Create(ctx, dst_gpu_addr, value, callback_1, callback_2, args);
}
Label* LabelCreate(GraphicContext* ctx, uint32_t* dst_gpu_addr, uint32_t value, LabelGpuObject::callback_t callback_1,
LabelGpuObject::callback_t callback_2, const uint64_t* args)
{
EXIT_IF(g_label_manager == nullptr);
return g_label_manager->Create(ctx, dst_gpu_addr, value, callback_1, callback_2, args);
}
void LabelDelete(Label* label)
{
EXIT_IF(g_label_manager == nullptr);
g_label_manager->Delete(label);
}
void LabelSet(CommandBuffer* buffer, Label* label)
{
EXIT_IF(g_label_manager == nullptr);
g_label_manager->Set(buffer, label);
}
void* LabelGpuObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* /*mem*/) const
{
KYTY_PROFILER_BLOCK("LabelGpuObject::Create");
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr || *vaddr == 0);
EXIT_NOT_IMPLEMENTED(*size != 8 && *size != 4);
auto value = params[PARAM_VALUE];
auto callback_1 = reinterpret_cast<LabelGpuObject::callback_t>(params[PARAM_CALLBACK_1]);
auto callback_2 = reinterpret_cast<LabelGpuObject::callback_t>(params[PARAM_CALLBACK_2]);
auto* label_obj =
(*size == 8 ? LabelCreate(ctx, reinterpret_cast<uint64_t*>(*vaddr), value, callback_1, callback_2, params + PARAM_ARG_1)
: (*size == 4 ? LabelCreate(ctx, reinterpret_cast<uint32_t*>(*vaddr), static_cast<uint32_t>(value), callback_1,
callback_2, params + PARAM_ARG_1)
: nullptr));
EXIT_NOT_IMPLEMENTED(label_obj == nullptr);
return label_obj;
}
static void update_func(GraphicContext* /*ctx*/, const uint64_t* /*params*/, void* /*obj*/, const uint64_t* /*vaddr*/,
const uint64_t* /*size*/, int /*vaddr_num*/)
{
KYTY_PROFILER_BLOCK("LabelGpuObject::update_func");
KYTY_NOT_IMPLEMENTED;
}
bool LabelGpuObject::Equal(const uint64_t* other) const
{
return (params[PARAM_VALUE] == other[PARAM_VALUE] && params[PARAM_CALLBACK_1] == other[PARAM_CALLBACK_1] &&
params[PARAM_CALLBACK_2] == other[PARAM_CALLBACK_2] && params[PARAM_ARG_1] == other[PARAM_ARG_1] &&
params[PARAM_ARG_2] == other[PARAM_ARG_2] && params[PARAM_ARG_3] == other[PARAM_ARG_3] &&
params[PARAM_ARG_4] == other[PARAM_ARG_4]);
}
static void delete_func(GraphicContext* /*ctx*/, void* obj, VulkanMemory* /*mem*/)
{
KYTY_PROFILER_BLOCK("LabelGpuObject::delete_func");
auto* label_obj = reinterpret_cast<Label*>(obj);
EXIT_IF(label_obj == nullptr);
LabelDelete(label_obj);
}
GpuObject::delete_func_t LabelGpuObject::GetDeleteFunc() const
{
return delete_func;
}
GpuObject::update_func_t LabelGpuObject::GetUpdateFunc() const
{
return update_func;
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED
+150
View File
@@ -0,0 +1,150 @@
#include "Emulator/Graphics/Pm4.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/File.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics::Pm4 {
static const char* g_names[256] = {};
static const char* g_r_names[64] = {};
static bool g_names_initialized = false;
static void init_names()
{
if (!g_names_initialized)
{
for (auto& n: g_names)
{
n = "<unknown>";
}
for (auto& n: g_r_names)
{
n = "<unknown>";
}
g_r_names[R_ZERO] = "R_ZERO";
g_r_names[R_VS] = "R_VS";
g_r_names[R_PS] = "R_PS";
g_r_names[R_DRAW_INDEX] = "R_DRAW_INDEX";
g_r_names[R_DRAW_INDEX_AUTO] = "R_DRAW_INDEX_AUTO";
g_r_names[R_DRAW_RESET] = "R_DRAW_RESET";
g_r_names[R_WAIT_FLIP_DONE] = "R_WAIT_FLIP_DONE";
g_r_names[R_CS] = "R_CS";
g_r_names[R_DISPATCH_DIRECT] = "R_DISPATCH_DIRECT";
g_r_names[R_DISPATCH_RESET] = "R_DISPATCH_RESET";
g_r_names[R_DISPATCH_WAIT_MEM] = "R_DISPATCH_WAIT_MEM";
g_r_names[R_PUSH_MARKER] = "R_PUSH_MARKER";
g_r_names[R_POP_MARKER] = "R_POP_MARKER";
g_r_names[R_VS_EMBEDDED] = "R_VS_EMBEDDED";
g_names[IT_NOP] = "IT_NOP";
g_names[IT_SET_BASE] = "IT_SET_BASE";
g_names[IT_CLEAR_STATE] = "IT_CLEAR_STATE";
g_names[IT_INDEX_BUFFER_SIZE] = "IT_INDEX_BUFFER_SIZE";
g_names[IT_DISPATCH_DIRECT] = "IT_DISPATCH_DIRECT";
g_names[IT_DISPATCH_INDIRECT] = "IT_DISPATCH_INDIRECT";
g_names[IT_SET_PREDICATION] = "IT_SET_PREDICATION";
g_names[IT_COND_EXEC] = "IT_COND_EXEC";
g_names[IT_DRAW_INDIRECT] = "IT_DRAW_INDIRECT";
g_names[IT_DRAW_INDEX_INDIRECT] = "IT_DRAW_INDEX_INDIRECT";
g_names[IT_INDEX_BASE] = "IT_INDEX_BASE";
g_names[IT_DRAW_INDEX_2] = "IT_DRAW_INDEX_2";
g_names[IT_CONTEXT_CONTROL] = "IT_CONTEXT_CONTROL";
g_names[IT_INDEX_TYPE] = "IT_INDEX_TYPE";
g_names[IT_DRAW_INDIRECT_MULTI] = "IT_DRAW_INDIRECT_MULTI";
g_names[IT_DRAW_INDEX_AUTO] = "IT_DRAW_INDEX_AUTO";
g_names[IT_NUM_INSTANCES] = "IT_NUM_INSTANCES";
g_names[IT_INDIRECT_BUFFER_CNST] = "IT_INDIRECT_BUFFER_CNST";
g_names[IT_DRAW_INDEX_OFFSET_2] = "IT_DRAW_INDEX_OFFSET_2";
g_names[IT_WRITE_DATA] = "IT_WRITE_DATA";
g_names[IT_MEM_SEMAPHORE] = "IT_MEM_SEMAPHORE";
g_names[IT_DRAW_INDEX_INDIRECT_MULTI] = "IT_DRAW_INDEX_INDIRECT_MULTI";
g_names[IT_WAIT_REG_MEM] = "IT_WAIT_REG_MEM";
g_names[IT_INDIRECT_BUFFER] = "IT_INDIRECT_BUFFER";
g_names[IT_COPY_DATA] = "IT_COPY_DATA";
g_names[IT_CP_DMA] = "IT_CP_DMA";
g_names[IT_PFP_SYNC_ME] = "IT_PFP_SYNC_ME";
g_names[IT_SURFACE_SYNC] = "IT_SURFACE_SYNC";
g_names[IT_EVENT_WRITE] = "IT_EVENT_WRITE";
g_names[IT_EVENT_WRITE_EOP] = "IT_EVENT_WRITE_EOP";
g_names[IT_EVENT_WRITE_EOS] = "IT_EVENT_WRITE_EOS";
g_names[IT_RELEASE_MEM] = "IT_RELEASE_MEM";
g_names[IT_DMA_DATA] = "IT_DMA_DATA";
g_names[IT_ACQUIRE_MEM] = "IT_ACQUIRE_MEM";
g_names[IT_REWIND] = "IT_REWIND";
g_names[IT_SET_CONFIG_REG] = "IT_SET_CONFIG_REG";
g_names[IT_SET_CONTEXT_REG] = "IT_SET_CONTEXT_REG";
g_names[IT_SET_SH_REG] = "IT_SET_SH_REG";
g_names[IT_SET_QUEUE_REG] = "IT_SET_QUEUE_REG";
g_names[IT_SET_UCONFIG_REG] = "IT_SET_UCONFIG_REG";
g_names[IT_WRITE_CONST_RAM] = "IT_WRITE_CONST_RAM";
g_names[IT_DUMP_CONST_RAM] = "IT_DUMP_CONST_RAM";
g_names[IT_INCREMENT_CE_COUNTER] = "IT_INCREMENT_CE_COUNTER";
g_names[IT_INCREMENT_DE_COUNTER] = "IT_INCREMENT_DE_COUNTER";
g_names[IT_WAIT_ON_CE_COUNTER] = "IT_WAIT_ON_CE_COUNTER";
g_names[IT_WAIT_ON_DE_COUNTER_DIFF] = "IT_WAIT_ON_DE_COUNTER_DIFF";
g_names[IT_DISPATCH_DRAW_PREAMBLE] = "IT_DISPATCH_DRAW_PREAMBLE";
g_names[IT_DISPATCH_DRAW] = "IT_DISPATCH_DRAW";
g_names_initialized = true;
}
}
void DumpPm4PacketStream(Core::File* file, uint32_t* cmd_buffer, uint32_t start_dw, uint32_t num_dw)
{
init_names();
// db_dump();
file->Printf("----- Buffer --- dwords: 0x%05" PRIx32 ", offset : %u, addr: %016" PRIx64 " ----- \n", num_dw, start_dw,
reinterpret_cast<uint64_t>(cmd_buffer));
auto* cmd = cmd_buffer + start_dw;
auto dw = num_dw;
for (;;)
{
if (dw == 0)
{
break;
}
EXIT_NOT_IMPLEMENTED(dw < 2);
EXIT_NOT_IMPLEMENTED(dw > num_dw);
auto cmd_id = *cmd++;
file->Printf("%05" PRIx32 " | 0x%08" PRIx32 " | ", start_dw, cmd_id);
uint32_t len = 0;
if ((cmd_id & 0xC0000000u) == 0xC0000000u)
{
bool sh_gx = (cmd_id & 0x2u) == 0;
len = ((cmd_id >> 16u) & 0x3fffu) + 1;
uint8_t op = ((cmd_id >> 8u) & 0xffu);
auto r = ((cmd_id >> 2u) & 0x3fu);
file->Printf("%s %s(OP:0x%02" PRIx8 ") SH:%s CNT:%u\n", g_names[op], (op == IT_NOP ? g_r_names[r] : ""), op,
sh_gx ? "GX" : "CX", len);
for (uint32_t i = 0; i < len; i++)
{
file->Printf(" | 0x%08" PRIx32 " | \n", cmd[i]);
}
} else
{
printf("?????\n");
}
cmd += len;
dw -= len + 1;
start_dw += len + 1;
}
}
} // namespace Kyty::Libs::Graphics::Pm4
#endif // KYTY_EMU_ENABLED
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,123 @@
#include "Emulator/Graphics/StorageBuffer.h"
#include "Kyty/Core/DbgAssert.h"
#include "Emulator/Graphics/GraphicContext.h"
#include "Emulator/Graphics/Utils.h"
#include "Emulator/Profiler.h"
#include "vulkan/vulkan_core.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
void* StorageBufferGpuObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num,
VulkanMemory* mem) const
{
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::Create");
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr || *vaddr == 0);
EXIT_IF(mem == nullptr);
EXIT_IF(ctx == nullptr);
auto* vk_obj = new VulkanBuffer;
vk_obj->usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
vk_obj->memory.property = static_cast<uint32_t>(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
vk_obj->buffer = nullptr;
VulkanCreateBuffer(ctx, *size, vk_obj);
EXIT_NOT_IMPLEMENTED(vk_obj->buffer == nullptr);
GetUpdateFunc()(ctx, params, vk_obj, vaddr, size, vaddr_num);
return vk_obj;
}
static void update_func(GraphicContext* ctx, const uint64_t* /*params*/, void* obj, const uint64_t* vaddr, const uint64_t* size,
int vaddr_num)
{
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::update_func");
EXIT_IF(ctx == nullptr);
EXIT_IF(obj == nullptr);
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num != 1);
auto* vk_obj = reinterpret_cast<VulkanBuffer*>(obj);
void* data = nullptr;
// vkMapMemory(ctx->device, vk_obj->memory.memory, vk_obj->memory.offset, *size, 0, &data);
VulkanMapMemory(ctx, &vk_obj->memory, &data);
memcpy(data, reinterpret_cast<void*>(*vaddr), *size);
// vkUnmapMemory(ctx->device, vk_obj->memory.memory);
VulkanUnmapMemory(ctx, &vk_obj->memory);
}
bool StorageBufferGpuObject::Equal(const uint64_t* other) const
{
return params[0] == other[0] && params[1] == other[1];
}
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* /*mem*/)
{
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::delete_func");
auto* vk_obj = reinterpret_cast<VulkanBuffer*>(obj);
EXIT_IF(vk_obj == nullptr);
EXIT_IF(vk_obj->buffer == nullptr);
EXIT_IF(ctx == nullptr);
VulkanDeleteBuffer(ctx, vk_obj);
delete vk_obj;
}
static void write_back(GraphicContext* ctx, void* obj, const uint64_t* vaddr, const uint64_t* size, int vaddr_num)
{
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::write_back");
EXIT_IF(ctx == nullptr);
EXIT_IF(obj == nullptr);
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num != 1);
auto* vk_obj = reinterpret_cast<VulkanBuffer*>(obj);
void* data = nullptr;
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::write_back::vkMapMemory");
// vkMapMemory(ctx->device, vk_obj->memory.memory, vk_obj->memory.offset, *size, 0, &data);
VulkanMapMemory(ctx, &vk_obj->memory, &data);
KYTY_PROFILER_END_BLOCK;
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::write_back::memcpy");
memcpy(reinterpret_cast<void*>(*vaddr), data, *size);
KYTY_PROFILER_END_BLOCK;
KYTY_PROFILER_BLOCK("StorageBufferGpuObject::write_back::vkUnmapMemory");
// vkUnmapMemory(ctx->device, vk_obj->memory.memory);
VulkanUnmapMemory(ctx, &vk_obj->memory);
KYTY_PROFILER_END_BLOCK;
}
GpuObject::write_back_func_t StorageBufferGpuObject::GetWriteBackFunc() const
{
return write_back;
}
GpuObject::delete_func_t StorageBufferGpuObject::GetDeleteFunc() const
{
return delete_func;
}
GpuObject::update_func_t StorageBufferGpuObject::GetUpdateFunc() const
{
return update_func;
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED
+244
View File
@@ -0,0 +1,244 @@
#include "Emulator/Graphics/Texture.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Config.h"
#include "Emulator/Graphics/GraphicContext.h"
#include "Emulator/Graphics/GraphicsRender.h"
#include "Emulator/Graphics/Tile.h"
#include "Emulator/Graphics/Utils.h"
#include "Emulator/Profiler.h"
#include <vulkan/vulkan_core.h>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
static VkFormat get_texture_format(uint32_t dfmt, uint32_t nfmt)
{
if (nfmt == 9 && dfmt == 10)
{
return VK_FORMAT_R8G8B8A8_SRGB;
}
if (nfmt == 9 && dfmt == 37)
{
return VK_FORMAT_BC3_SRGB_BLOCK;
}
EXIT("unknown format: nfmt = %u, dfmt = %u\n", nfmt, dfmt);
return VK_FORMAT_UNDEFINED;
}
static VkComponentSwizzle get_swizzle(uint8_t s)
{
switch (s)
{
case 0: return VK_COMPONENT_SWIZZLE_ZERO; break;
case 1: return VK_COMPONENT_SWIZZLE_ONE; break;
case 4: return VK_COMPONENT_SWIZZLE_R; break;
case 5: return VK_COMPONENT_SWIZZLE_G; break;
case 6: return VK_COMPONENT_SWIZZLE_B; break;
case 7: return VK_COMPONENT_SWIZZLE_A; break;
case 2:
case 3:
default: EXIT("unknown swizzle: %d\n", static_cast<int>(s));
}
return VK_COMPONENT_SWIZZLE_IDENTITY;
}
void* TextureObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const
{
KYTY_PROFILER_BLOCK("TextureObject::Create");
EXIT_IF(size == nullptr || vaddr == nullptr);
EXIT_IF(mem == nullptr);
EXIT_IF(ctx == nullptr);
auto dfmt = params[PARAM_DFMT];
auto nfmt = params[PARAM_NFMT];
auto width = params[PARAM_WIDTH];
auto height = params[PARAM_HEIGHT];
auto levels = params[PARAM_LEVELS];
auto swizzle = params[PARAM_SWIZZLE];
auto pixel_format = get_texture_format(dfmt, nfmt);
EXIT_NOT_IMPLEMENTED(pixel_format == VK_FORMAT_UNDEFINED);
EXIT_NOT_IMPLEMENTED(width == 0);
EXIT_NOT_IMPLEMENTED(height == 0);
auto* vk_obj = new TextureVulkanImage;
vk_obj->extent.width = width;
vk_obj->extent.height = height;
vk_obj->format = pixel_format;
vk_obj->image = nullptr;
vk_obj->image_view = nullptr;
VkImageCreateInfo image_info {};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.pNext = nullptr;
image_info.flags = 0;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.extent.width = vk_obj->extent.width;
image_info.extent.height = vk_obj->extent.height;
image_info.extent.depth = 1;
image_info.mipLevels = levels;
image_info.arrayLayers = 1;
image_info.format = vk_obj->format;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
vkCreateImage(ctx->device, &image_info, nullptr, &vk_obj->image);
EXIT_NOT_IMPLEMENTED(vk_obj->image == nullptr);
vkGetImageMemoryRequirements(ctx->device, vk_obj->image, &mem->requirements);
mem->property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
bool allocated = VulkanAllocate(ctx, mem);
EXIT_NOT_IMPLEMENTED(!allocated);
VulkanBindImageMemory(ctx, vk_obj, mem);
vk_obj->memory = *mem;
// EXIT_NOT_IMPLEMENTED(mem->requirements.size > *size);
GetUpdateFunc()(ctx, params, vk_obj, vaddr, size, vaddr_num);
VkImageViewCreateInfo create_info {};
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
create_info.pNext = nullptr;
create_info.flags = 0;
create_info.image = vk_obj->image;
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
create_info.format = vk_obj->format;
create_info.components.r = get_swizzle(swizzle & 0xffu);
create_info.components.g = get_swizzle((swizzle >> 8u) & 0xffu);
create_info.components.b = get_swizzle((swizzle >> 16u) & 0xffu);
create_info.components.a = get_swizzle((swizzle >> 24u) & 0xffu);
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
create_info.subresourceRange.baseArrayLayer = 0;
create_info.subresourceRange.baseMipLevel = 0;
create_info.subresourceRange.layerCount = 1;
create_info.subresourceRange.levelCount = 1;
vkCreateImageView(ctx->device, &create_info, nullptr, &vk_obj->image_view);
EXIT_NOT_IMPLEMENTED(vk_obj->image_view == nullptr);
return vk_obj;
}
static void update_func(GraphicContext* ctx, const uint64_t* params, void* obj, const uint64_t* vaddr, const uint64_t* size, int vaddr_num)
{
KYTY_PROFILER_BLOCK("TextureObject::update_func");
EXIT_IF(obj == nullptr);
EXIT_IF(ctx == nullptr);
EXIT_IF(params == nullptr);
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num != 1);
auto* vk_obj = static_cast<TextureVulkanImage*>(obj);
bool tile = (params[TextureObject::PARAM_TILE] != 0);
auto dfmt = params[TextureObject::PARAM_DFMT];
auto nfmt = params[TextureObject::PARAM_NFMT];
auto width = params[TextureObject::PARAM_WIDTH];
auto height = params[TextureObject::PARAM_HEIGHT];
auto levels = params[TextureObject::PARAM_LEVELS];
bool neo = Config::IsNeo();
EXIT_NOT_IMPLEMENTED(levels >= 16);
uint32_t level_sizes[16];
TileGetTextureSize(dfmt, nfmt, width, height, levels, tile, neo, nullptr, level_sizes, nullptr, nullptr);
// dbg_test_mipmaps(ctx, VK_FORMAT_BC3_SRGB_BLOCK, 512, 512);
uint32_t offset = 0;
uint32_t mip_width = width;
uint32_t mip_height = height;
Vector<BufferImageCopy> regions(levels);
for (uint32_t i = 0; i < levels; i++)
{
EXIT_NOT_IMPLEMENTED(level_sizes[i] == 0);
regions[i].offset = offset;
regions[i].width = mip_width;
regions[i].height = mip_height;
offset += level_sizes[i];
if (mip_width > 1)
{
mip_width /= 2;
}
if (mip_height > 1)
{
mip_height /= 2;
}
}
if (tile)
{
auto* temp_buf = new uint8_t[*size];
TileConvertTiledToLinear(temp_buf, reinterpret_cast<void*>(*vaddr), TileMode::TextureTiled, dfmt, nfmt, width, height, levels, neo);
UtilFillImage(ctx, vk_obj, temp_buf, *size, regions);
delete[] temp_buf;
} else
{
UtilFillImage(ctx, vk_obj, reinterpret_cast<void*>(*vaddr), *size, regions);
}
}
bool TextureObject::Equal(const uint64_t* other) const
{
return (params[PARAM_DFMT] == other[PARAM_DFMT] && params[PARAM_NFMT] == other[PARAM_NFMT] &&
params[PARAM_WIDTH] == other[PARAM_WIDTH] && params[PARAM_HEIGHT] == other[PARAM_HEIGHT] &&
params[PARAM_LEVELS] == other[PARAM_LEVELS] && params[PARAM_TILE] == other[PARAM_TILE] &&
params[PARAM_NEO] == other[PARAM_NEO] && params[PARAM_SWIZZLE] == other[PARAM_SWIZZLE]);
}
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* mem)
{
KYTY_PROFILER_BLOCK("TextureObject::delete_func");
auto* vk_obj = reinterpret_cast<TextureVulkanImage*>(obj);
EXIT_IF(vk_obj == nullptr);
EXIT_IF(ctx == nullptr);
DeleteDescriptor(vk_obj);
vkDestroyImageView(ctx->device, vk_obj->image_view, nullptr);
vkDestroyImage(ctx->device, vk_obj->image, nullptr);
VulkanFree(ctx, mem);
delete vk_obj;
}
GpuObject::delete_func_t TextureObject::GetDeleteFunc() const
{
return delete_func;
}
GpuObject::update_func_t TextureObject::GetUpdateFunc() const
{
return update_func;
}
} // namespace Kyty::Libs::Graphics
#endif
+674
View File
@@ -0,0 +1,674 @@
#include "Emulator/Graphics/Tile.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Threads.h"
#include "Emulator/Graphics/AsyncJob.h"
#include "Emulator/Profiler.h"
#if KYTY_COMPILER != KYTY_COMPILER_CLANG
#include <intrin.h>
#endif
#include <algorithm>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
struct Uint128
{
uint64_t n[2];
};
struct Uint256
{
Uint128 n[2];
};
class Tiler
{
public:
Tiler(): m_job1(nullptr), m_job2(nullptr) /*, m_job3(nullptr), m_job4(nullptr)*/
{
EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
}
virtual ~Tiler() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(Tiler);
Core::Mutex m_mutex;
AsyncJob m_job1;
AsyncJob m_job2;
// AsyncJob m_job3;
// AsyncJob m_job4;
};
class Tiler32
{
public:
uint32_t m_macro_tile_height = 0;
uint32_t m_bank_height = 0;
uint32_t m_num_banks = 0;
uint32_t m_num_pipes = 0;
uint32_t m_padded_width = 0;
uint32_t m_padded_height = 0;
uint32_t m_pipe_bits = 0;
uint32_t m_bank_bits = 0;
void Init(uint32_t width, uint32_t height, bool neo)
{
m_macro_tile_height = (neo ? 128 : 64);
m_bank_height = neo ? 2 : 1;
m_num_banks = neo ? 8 : 16;
m_num_pipes = neo ? 16 : 8;
m_padded_width = width;
if (height == 1080)
{
m_padded_height = neo ? 1152 : 1088;
}
if (height == 720)
{
m_padded_height = 768;
}
m_pipe_bits = neo ? 4 : 3;
m_bank_bits = neo ? 3 : 4;
}
static uint32_t GetElementIndex(uint32_t x, uint32_t y)
{
uint32_t elem = 0;
elem |= ((x >> 0u) & 0x1u) << 0u;
elem |= ((x >> 1u) & 0x1u) << 1u;
elem |= ((y >> 0u) & 0x1u) << 2u;
elem |= ((x >> 2u) & 0x1u) << 3u;
elem |= ((y >> 1u) & 0x1u) << 4u;
elem |= ((y >> 2u) & 0x1u) << 5u;
return elem;
}
static uint32_t GetPipeIndex(uint32_t x, uint32_t y, bool neo)
{
uint32_t pipe = 0;
if (!neo)
{
pipe |= (((x >> 3u) ^ (y >> 3u) ^ (x >> 4u)) & 0x1u) << 0u;
pipe |= (((x >> 4u) ^ (y >> 4u)) & 0x1u) << 1u;
pipe |= (((x >> 5u) ^ (y >> 5u)) & 0x1u) << 2u;
} else
{
pipe |= (((x >> 3u) ^ (y >> 3u) ^ (x >> 4u)) & 0x1u) << 0u;
pipe |= (((x >> 4u) ^ (y >> 4u)) & 0x1u) << 1u;
pipe |= (((x >> 5u) ^ (y >> 5u)) & 0x1u) << 2u;
pipe |= (((x >> 6u) ^ (y >> 5u)) & 0x1u) << 3u;
}
return pipe;
}
static uint32_t IntLog2(uint32_t i)
{
#if KYTY_COMPILER == KYTY_COMPILER_CLANG
return 31 - __builtin_clz(i | 1u);
#else
unsigned long temp;
_BitScanReverse(&temp, i | 1u);
return temp;
#endif
}
static uint32_t GetBankIndex(uint32_t x, uint32_t y, uint32_t bank_width, uint32_t bank_height, uint32_t num_banks, uint32_t num_pipes)
{
const uint32_t x_shift_offset = IntLog2(bank_width * num_pipes);
const uint32_t y_shift_offset = IntLog2(bank_height);
const uint32_t xs = x >> x_shift_offset;
const uint32_t ys = y >> y_shift_offset;
uint32_t bank = 0;
switch (num_banks)
{
case 8:
bank |= (((xs >> 3u) ^ (ys >> 5u)) & 0x1u) << 0u;
bank |= (((xs >> 4u) ^ (ys >> 4u) ^ (ys >> 5u)) & 0x1u) << 1u;
bank |= (((xs >> 5u) ^ (ys >> 3u)) & 0x1u) << 2u;
break;
case 16:
bank |= (((xs >> 3u) ^ (ys >> 6u)) & 0x1u) << 0u;
bank |= (((xs >> 4u) ^ (ys >> 5u) ^ (ys >> 6u)) & 0x1u) << 1u;
bank |= (((xs >> 5u) ^ (ys >> 4u)) & 0x1u) << 2u;
bank |= (((xs >> 6u) ^ (ys >> 3u)) & 0x1u) << 3u;
break;
default:;
}
return bank;
}
[[nodiscard]] uint64_t GetTiledOffset(uint32_t x, uint32_t y, bool neo) const
{
uint64_t element_index = GetElementIndex(x, y);
uint32_t xh = x;
uint32_t yh = y;
uint64_t pipe = GetPipeIndex(xh, yh, neo);
uint64_t bank = GetBankIndex(xh, yh, 1, m_bank_height, m_num_banks, m_num_pipes);
uint32_t tile_bytes = (8 * 8 * 32 + 7) / 8;
uint64_t element_offset = (element_index * 32);
uint64_t tile_split_slice = 0;
if (tile_bytes > 512)
{
tile_split_slice = element_offset / (512 * 8);
element_offset %= (512 * 8);
tile_bytes = 512;
}
uint64_t macro_tile_bytes = (128 / 8) * (m_macro_tile_height / 8) * tile_bytes / (m_num_pipes * m_num_banks);
uint64_t macro_tiles_per_row = m_padded_width / 128;
uint64_t macro_tile_row_index = y / m_macro_tile_height;
uint64_t macro_tile_column_index = x / 128;
uint64_t macro_tile_index = (macro_tile_row_index * macro_tiles_per_row) + macro_tile_column_index;
uint64_t macro_tile_offset = macro_tile_index * macro_tile_bytes;
uint64_t macro_tiles_per_slice = macro_tiles_per_row * (m_padded_height / m_macro_tile_height);
uint64_t slice_bytes = macro_tiles_per_slice * macro_tile_bytes;
uint64_t slice_offset = tile_split_slice * slice_bytes;
uint64_t tile_row_index = (y / 8) % m_bank_height;
uint64_t tile_index = tile_row_index;
uint64_t tile_offset = tile_index * tile_bytes;
uint64_t tile_split_slice_rotation = ((m_num_banks / 2) + 1) * tile_split_slice;
bank ^= tile_split_slice_rotation;
bank &= (m_num_banks - 1);
uint64_t total_offset = (slice_offset + macro_tile_offset + tile_offset) * 8 + element_offset;
uint64_t bit_offset = total_offset & 0x7u;
total_offset /= 8;
uint64_t pipe_interleave_offset = total_offset & 0xffu;
uint64_t offset = total_offset >> 8u;
uint64_t byte_offset =
pipe_interleave_offset | (pipe << (8u)) | (bank << (8u + m_pipe_bits)) | (offset << (8u + m_pipe_bits + m_bank_bits));
return ((byte_offset << 3u) | bit_offset) / 8;
}
};
class Tiler1d
{
public:
uint32_t m_width = 0;
uint32_t m_height = 0;
uint32_t m_bits_per_element = 0;
uint32_t m_tile_bytes = 0;
uint32_t m_tiles_per_row = 0;
void Init(uint32_t dfmt, uint32_t nfmt, uint32_t width, uint32_t height, uint32_t padded_width, uint32_t /*padded_height*/,
bool /*neo*/)
{
m_width = width;
m_height = height;
if (nfmt == 9 && dfmt == 10)
{
// VK_FORMAT_R8G8B8A8_SRGB;
m_bits_per_element = 32;
} else if (nfmt == 9 && dfmt == 37)
{
// VK_FORMAT_BC3_SRGB_BLOCK;
m_bits_per_element = 128;
m_width = std::max((m_width + 3) / 4, 1U);
m_height = std::max((m_height + 3) / 4, 1U);
} else
{
EXIT("unknown format: nfmt = %u, dfmt = %u\n", nfmt, dfmt);
}
m_tile_bytes = (8 * 8 * 1 * m_bits_per_element + 7) / 8;
m_tiles_per_row = padded_width / 8;
}
static uint32_t GetElementIndex(uint32_t x, uint32_t y)
{
uint32_t elem = 0;
elem |= ((x >> 0u) & 0x1u) << 0u;
elem |= ((y >> 0u) & 0x1u) << 1u;
elem |= ((x >> 1u) & 0x1u) << 2u;
elem |= ((y >> 1u) & 0x1u) << 3u;
elem |= ((x >> 2u) & 0x1u) << 4u;
elem |= ((y >> 2u) & 0x1u) << 5u;
return elem;
}
[[nodiscard]] uint64_t GetTiledOffset(uint32_t x, uint32_t y, bool /*neo*/) const
{
uint64_t element_index = GetElementIndex(x, y);
uint64_t tile_row_index = y / 8;
uint64_t tile_column_index = x / 8;
uint64_t tile_offset = ((tile_row_index * m_tiles_per_row) + tile_column_index) * m_tile_bytes;
uint64_t element_offset = element_index * m_bits_per_element;
uint64_t offset = tile_offset * 8 + element_offset;
return offset / 8;
}
};
static Tiler* g_tiler = nullptr;
void TileInit()
{
EXIT_IF(g_tiler != nullptr);
g_tiler = new Tiler;
}
// NOLINTNEXTLINE(readability-non-const-parameter)
static void Detile32(const Tiler32* t, uint32_t width, uint32_t height, uint32_t dst_pitch, uint8_t* dst, const uint8_t* src, bool neo)
{
EXIT_IF(g_tiler == nullptr);
Core::LockGuard lock(g_tiler->m_mutex);
struct DetileParams
{
const Tiler32* t;
uint32_t start_y;
uint32_t width;
uint32_t height;
uint32_t dst_pitch;
uint8_t* dst;
const uint8_t* src;
bool neo;
};
auto func = [](void* args)
{
auto* p = static_cast<DetileParams*>(args);
auto* dst = p->dst;
const auto* src = p->src;
const Tiler32* t = p->t;
uint32_t start_y = p->start_y;
uint32_t width = p->width;
uint32_t height = p->height;
uint32_t dst_pitch = p->dst_pitch;
bool neo = p->neo;
for (uint32_t y = start_y; y < height; y++)
{
uint32_t x = 0;
uint64_t linear_offset = y * dst_pitch * 4;
for (; x + 1 < width; x += 2)
{
auto tiled_offset = t->GetTiledOffset(x, y, neo);
*reinterpret_cast<uint64_t*>(dst + linear_offset) = *reinterpret_cast<const uint64_t*>(src + tiled_offset);
linear_offset += 8;
}
if (x < width)
{
auto tiled_offset = t->GetTiledOffset(x, y, neo);
*reinterpret_cast<uint32_t*>(dst + linear_offset) = *reinterpret_cast<const uint32_t*>(src + tiled_offset);
}
}
};
DetileParams p1 {t, 0, width, height / 4, dst_pitch, dst, src, neo};
DetileParams p2 {t, p1.height, width, /*(height * 2) / 4*/ height, dst_pitch, dst, src, neo};
// DetileParams p3 {t, p2.height, width, (height * 3) / 4, dst_pitch, dst, src, neo};
// DetileParams p4 {t, p3.height, width, height, dst_pitch, dst, src, neo};
g_tiler->m_job1.Execute(func, &p1);
g_tiler->m_job2.Execute(func, &p2);
// g_tiler->m_job3.Execute(func, &p3);
// g_tiler->m_job4.Execute(func, &p4);
g_tiler->m_job1.Wait();
g_tiler->m_job2.Wait();
// g_tiler->m_job3.Wait();
// g_tiler->m_job4.Wait();
// Core::Thread t1(func, &p1);
// Core::Thread t2(func, &p2);
// Core::Thread t3(func, &p3);
// Core::Thread t4(func, &p4);
//
// t1.Join();
// t2.Join();
// t3.Join();
// t4.Join();
}
static void Detile32(const Tiler1d* t, uint32_t width, uint32_t height, uint32_t dst_pitch, uint8_t* dst, const uint8_t* src, bool neo)
{
for (uint32_t y = 0; y < height; y++)
{
uint32_t x = 0;
uint64_t linear_offset = y * dst_pitch * 4;
for (; x + 1 < width; x += 2)
{
auto tiled_offset = t->GetTiledOffset(x, y, neo);
*reinterpret_cast<uint64_t*>(dst + linear_offset) = *reinterpret_cast<const uint64_t*>(src + tiled_offset);
linear_offset += 8;
}
if (x < width)
{
auto tiled_offset = t->GetTiledOffset(x, y, neo);
*reinterpret_cast<uint32_t*>(dst + linear_offset) = *reinterpret_cast<const uint32_t*>(src + tiled_offset);
}
}
}
static void Detile128(const Tiler1d* t, uint32_t width, uint32_t height, uint32_t dst_pitch, uint8_t* dst, const uint8_t* src, bool neo)
{
for (uint32_t y = 0; y < height; y++)
{
uint32_t x = 0;
uint64_t linear_offset = y * dst_pitch * 16;
for (; x + 1 < width; x += 2)
{
auto tiled_offset = t->GetTiledOffset(x, y, neo);
*reinterpret_cast<Uint256*>(dst + linear_offset) = *reinterpret_cast<const Uint256*>(src + tiled_offset);
linear_offset += 32;
}
if (x < width)
{
auto tiled_offset = t->GetTiledOffset(x, y, neo);
*reinterpret_cast<Uint128*>(dst + linear_offset) = *reinterpret_cast<const Uint128*>(src + tiled_offset);
}
}
}
static void Detile1d(const Tiler1d* t, uint8_t* dst, const uint8_t* src, bool neo)
{
if (t->m_bits_per_element == 32)
{
Detile32(t, t->m_width, t->m_height, t->m_width, dst, src, neo);
} else if (t->m_bits_per_element == 128)
{
Detile128(t, t->m_width, t->m_height, t->m_width, dst, src, neo);
} else
{
EXIT("Unknown size");
}
}
void TileConvertTiledToLinear(void* dst, const void* src, TileMode mode, uint32_t width, uint32_t height, bool neo)
{
KYTY_PROFILER_FUNCTION();
EXIT_NOT_IMPLEMENTED(mode != TileMode::VideoOutTiled);
Tiler32 t;
t.Init(width, height, neo);
Detile32(&t, width, height, width, static_cast<uint8_t*>(dst), static_cast<const uint8_t*>(src), neo);
}
void TileConvertTiledToLinear(void* dst, const void* src, TileMode mode, uint32_t dfmt, uint32_t nfmt, uint32_t width, uint32_t height,
uint32_t levels, bool neo)
{
EXIT_NOT_IMPLEMENTED(mode != TileMode::TextureTiled);
uint32_t padded_width[16] = {0};
uint32_t padded_height[16] = {0};
uint32_t level_sizes[16] = {0};
TileGetTextureSize(dfmt, nfmt, width, height, levels, true, neo, nullptr, level_sizes, padded_width, padded_height);
uint32_t mip_width = width;
uint32_t mip_height = height;
auto* dstptr = static_cast<uint8_t*>(dst);
const auto* srcptr = static_cast<const uint8_t*>(src);
for (int l = 0; l < levels; l++)
{
Tiler1d t;
t.Init(dfmt, nfmt, mip_width, mip_height, padded_width[l], padded_height[l], neo);
Detile1d(&t, dstptr, srcptr, neo);
dstptr += level_sizes[l];
srcptr += level_sizes[l];
if (mip_width > 1)
{
mip_width /= 2;
}
if (mip_height > 1)
{
mip_height /= 2;
}
}
}
void TileGetDepthSize(uint32_t width, uint32_t height, uint32_t z_format, uint32_t stencil_format, bool htile, bool neo,
uint32_t* stencil_size, uint32_t* htile_size, uint32_t* depth_size, uint32_t* pitch)
{
struct SizeAlign
{
uint32_t size;
uint32_t align;
};
struct DepthInfo
{
uint32_t width;
uint32_t height;
uint32_t z_format;
uint32_t stencil_format;
bool tile;
bool neo;
uint32_t pitch;
SizeAlign stencil;
SizeAlign htile;
SizeAlign depth;
};
static const DepthInfo infos_base[] = {
{1920, 1080, 3, 0, true, false, 2048, {0, 0}, {196608, 2048}, {9437184, 32768}},
{1920, 1080, 3, 0, false, false, 2048, {0, 0}, {0, 0}, {9437184, 32768}},
{1280, 720, 3, 0, true, false, 1280, {0, 0}, {98304, 2048}, {3932160, 32768}},
{1280, 720, 3, 0, false, false, 1280, {0, 0}, {0, 0}, {3932160, 32768}},
{1920, 1080, 1, 0, true, false, 2048, {0, 0}, {196608, 2048}, {4718592, 32768}},
{1920, 1080, 1, 0, false, false, 2048, {0, 0}, {0, 0}, {4718592, 32768}},
{1280, 720, 1, 0, true, false, 1280, {0, 0}, {98304, 2048}, {1966080, 32768}},
{1280, 720, 1, 0, false, false, 1280, {0, 0}, {0, 0}, {1966080, 32768}},
{1920, 1080, 0, 1, true, false, 2048, {2359296, 32768}, {196608, 2048}, {0, 0}},
{1920, 1080, 0, 1, false, false, 2048, {2359296, 32768}, {0, 0}, {0, 0}},
{1280, 720, 0, 1, true, false, 1280, {983040, 32768}, {98304, 2048}, {0, 0}},
{1280, 720, 0, 1, false, false, 1280, {983040, 32768}, {0, 0}, {0, 0}},
{1920, 1080, 3, 1, true, false, 2048, {2359296, 32768}, {196608, 2048}, {9437184, 32768}},
{1920, 1080, 3, 1, false, false, 2048, {2359296, 32768}, {0, 0}, {9437184, 32768}},
{1280, 720, 3, 1, true, false, 1280, {983040, 32768}, {98304, 2048}, {3932160, 32768}},
{1280, 720, 3, 1, false, false, 1280, {983040, 32768}, {0, 0}, {3932160, 32768}},
{1920, 1080, 1, 1, true, false, 2048, {2359296, 32768}, {196608, 2048}, {4718592, 32768}},
{1920, 1080, 1, 1, false, false, 2048, {2359296, 32768}, {0, 0}, {4718592, 32768}},
{1280, 720, 1, 1, true, false, 1280, {983040, 32768}, {98304, 2048}, {1966080, 32768}},
{1280, 720, 1, 1, false, false, 1280, {983040, 32768}, {0, 0}, {1966080, 32768}},
};
static const DepthInfo infos_neo[] = {
{1920, 1080, 3, 0, true, true, 1920, {0, 0}, {196608, 4096}, {8847360, 65536}},
{1920, 1080, 3, 0, false, true, 1920, {0, 0}, {0, 0}, {8847360, 65536}},
{1280, 720, 3, 0, true, true, 1280, {0, 0}, {131072, 4096}, {3932160, 65536}},
{1280, 720, 3, 0, false, true, 1280, {0, 0}, {0, 0}, {3932160, 65536}},
{1920, 1080, 1, 0, true, true, 2048, {0, 0}, {196608, 4096}, {4718592, 65536}},
{1920, 1080, 1, 0, false, true, 2048, {0, 0}, {0, 0}, {4718592, 65536}},
{1280, 720, 1, 0, true, true, 1280, {0, 0}, {131072, 4096}, {1966080, 65536}},
{1280, 720, 1, 0, false, true, 1280, {0, 0}, {0, 0}, {1966080, 65536}},
{1920, 1080, 0, 1, true, true, 2048, {2359296, 32768}, {196608, 4096}, {0, 0}},
{1920, 1080, 0, 1, false, true, 2048, {2359296, 32768}, {0, 0}, {0, 0}},
{1280, 720, 0, 1, true, true, 1280, {983040, 32768}, {131072, 4096}, {0, 0}},
{1280, 720, 0, 1, false, true, 1280, {983040, 32768}, {0, 0}, {0, 0}},
{1920, 1080, 3, 1, true, true, 2048, {2359296, 32768}, {196608, 4096}, {9437184, 65536}},
{1920, 1080, 3, 1, false, true, 2048, {2359296, 32768}, {0, 0}, {9437184, 65536}},
{1280, 720, 3, 1, true, true, 1280, {983040, 32768}, {131072, 4096}, {3932160, 65536}},
{1280, 720, 3, 1, false, true, 1280, {983040, 32768}, {0, 0}, {3932160, 65536}},
{1920, 1080, 1, 1, true, true, 2048, {2359296, 32768}, {196608, 4096}, {4718592, 65536}},
{1920, 1080, 1, 1, false, true, 2048, {2359296, 32768}, {0, 0}, {4718592, 65536}},
{1280, 720, 1, 1, true, true, 1280, {983040, 32768}, {131072, 4096}, {1966080, 65536}},
{1280, 720, 1, 1, false, true, 1280, {983040, 32768}, {0, 0}, {1966080, 65536}},
};
EXIT_IF(depth_size == nullptr);
EXIT_IF(htile_size == nullptr);
EXIT_IF(stencil_size == nullptr);
EXIT_IF(pitch == nullptr);
if (neo)
{
for (const auto& i: infos_neo)
{
if (i.width == width && i.height == height && i.tile == htile && i.z_format == z_format && i.stencil_format == stencil_format)
{
*depth_size = i.depth.size;
*htile_size = i.htile.size;
*stencil_size = i.stencil.size;
*pitch = i.pitch;
return;
}
}
} else
{
for (const auto& i: infos_base)
{
if (i.width == width && i.height == height && i.tile == htile && i.z_format == z_format && i.stencil_format == stencil_format)
{
*depth_size = i.depth.size;
*htile_size = i.htile.size;
*stencil_size = i.stencil.size;
*pitch = i.pitch;
return;
}
}
}
*depth_size = 0;
*htile_size = 0;
*stencil_size = 0;
}
void TileGetVideoOutSize(uint32_t width, uint32_t height, bool tile, bool neo, uint32_t* size)
{
EXIT_IF(size == nullptr);
if (width == 1920 && height == 1080 && tile && !neo)
{
*size = 8355840;
}
if (width == 1920 && height == 1080 && tile && neo)
{
*size = 8847360;
}
if (width == 1920 && height == 1080 && !tile && !neo)
{
*size = 8294400;
}
if (width == 1920 && height == 1080 && !tile && neo)
{
*size = 8294400;
}
if (width == 1280 && height == 720 && tile && !neo)
{
*size = 3932160;
}
if (width == 1280 && height == 720 && tile && neo)
{
*size = 3932160;
}
if (width == 1280 && height == 720 && !tile && !neo)
{
*size = 3686400;
}
if (width == 1280 && height == 720 && !tile && neo)
{
*size = 3686400;
}
}
void TileGetTextureSize(uint32_t dfmt, uint32_t nfmt, uint32_t width, uint32_t height, uint32_t levels, bool tile, bool neo,
uint32_t* total_size, uint32_t* level_sizes, uint32_t* padded_width, uint32_t* padded_height)
{
struct Padded
{
uint32_t width;
uint32_t height;
};
struct TextureInfo
{
uint32_t dfmt;
uint32_t nfmt;
uint32_t width;
uint32_t height;
uint32_t levels;
bool tile;
bool neo;
uint32_t size[16];
Padded padded[16];
};
static const TextureInfo infos[] = {
// clang-format off
{ 10, 9, 512, 512, 10, false, false, {1048576, 262144, 65536, 16384, 4096, 1024, 512, 256, 256, 256, },
{ {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, } },
{ 10, 9, 512, 512, 10, false, true, {1048576, 262144, 65536, 16384, 4096, 1024, 512, 256, 256, 256, },
{ {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, } },
{ 10, 9, 512, 512, 10, true, false, {1048576, 262144, 65536, 16384, 4096, 1024, 256, 256, 256, 256, },
{ {512, 512}, {256, 256}, {128, 128}, {64, 64}, {32, 32}, {16, 16}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, } },
{ 10, 9, 512, 512, 10, true, true, {1048576, 262144, 65536, 16384, 4096, 1024, 256, 256, 256, 256, },
{ {512, 512}, {256, 256}, {128, 128}, {64, 64}, {32, 32}, {16, 16}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, } },
{ 37, 9, 512, 512, 10, false, false, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
{ {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, } },
{ 37, 9, 512, 512, 10, false, true, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
{ {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, } },
{ 37, 9, 512, 512, 10, true, false, {262144, 65536, 16384, 4096, 1024, 1024, 1024, 1024, 1024, 1024, },
{ {128, 128}, {64, 64}, {32, 32}, {16, 16}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, } },
{ 37, 9, 512, 512, 10, true, true, {262144, 65536, 16384, 4096, 1024, 1024, 1024, 1024, 1024, 1024, },
{ {128, 128}, {64, 64}, {32, 32}, {16, 16}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, {8, 8}, } },
// clang-format on
};
// EXIT_IF(total_size == nullptr);
for (const auto& i: infos)
{
if (i.dfmt == dfmt && i.nfmt == nfmt && i.width == width && i.height == height && i.levels >= levels && i.tile == tile &&
i.neo == neo)
{
for (uint32_t l = 0; l < levels; l++)
{
if (total_size != nullptr)
{
*total_size += i.size[l];
}
if (level_sizes != nullptr)
{
level_sizes[l] = i.size[l];
}
if (padded_width != nullptr)
{
padded_width[l] = i.padded[l].width;
}
if (padded_height != nullptr)
{
padded_height[l] = i.padded[l].height;
}
}
}
}
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED
+372
View File
@@ -0,0 +1,372 @@
#include "Emulator/Graphics/Utils.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Graphics/GpuMemory.h"
#include "Emulator/Graphics/GraphicContext.h"
#include "Emulator/Graphics/GraphicsRender.h"
#include "Emulator/Profiler.h"
#include "vulkan/vulkan_core.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
static void set_image_layout(VkCommandBuffer buffer, VkImage image, uint32_t levels, VkImageAspectFlags aspect_mask,
VkImageLayout old_image_layout, VkImageLayout new_image_layout)
{
EXIT_IF(buffer == nullptr);
VkImageMemoryBarrier image_memory_barrier {};
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_memory_barrier.pNext = nullptr;
image_memory_barrier.srcAccessMask = 0;
image_memory_barrier.dstAccessMask = 0;
image_memory_barrier.oldLayout = old_image_layout;
image_memory_barrier.newLayout = new_image_layout;
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.image = image;
image_memory_barrier.subresourceRange.aspectMask = aspect_mask;
image_memory_barrier.subresourceRange.baseMipLevel = 0;
image_memory_barrier.subresourceRange.levelCount = levels;
image_memory_barrier.subresourceRange.baseArrayLayer = 0;
image_memory_barrier.subresourceRange.layerCount = 1;
if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
{
image_memory_barrier.srcAccessMask = 0; // VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
}
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
image_memory_barrier.dstAccessMask = 0; // VK_ACCESS_TRANSFER_WRITE_BIT;
}
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
image_memory_barrier.dstAccessMask = 0; // VK_ACCESS_TRANSFER_READ_BIT;
}
if (old_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
image_memory_barrier.srcAccessMask = 0; // VK_ACCESS_TRANSFER_WRITE_BIT;
}
if (old_image_layout == VK_IMAGE_LAYOUT_PREINITIALIZED)
{
image_memory_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
}
if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
image_memory_barrier.srcAccessMask = 0; /*VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT*/
image_memory_barrier.dstAccessMask = 0; // VK_ACCESS_SHADER_READ_BIT;
}
if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
{
image_memory_barrier.dstAccessMask = 0; // VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
}
if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
{
image_memory_barrier.dstAccessMask = 0; // VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
vkCmdPipelineBarrier(buffer, src_stages, dest_stages, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
}
void UtilBufferToImage(CommandBuffer* buffer, VulkanBuffer* src_buffer, VideoOutVulkanImage* dst_image)
{
EXIT_IF(src_buffer == nullptr);
EXIT_IF(src_buffer->buffer == nullptr);
EXIT_IF(dst_image == nullptr);
EXIT_IF(dst_image->image == nullptr);
auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()];
set_image_layout(vk_buffer, dst_image->image, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkBufferImageCopy region {};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = {0, 0, 0};
region.imageExtent = {dst_image->extent.width, dst_image->extent.height, 1};
vkCmdCopyBufferToImage(vk_buffer, src_buffer->buffer, dst_image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
set_image_layout(vk_buffer, dst_image->image, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
}
void UtilBufferToImage(CommandBuffer* buffer, VulkanBuffer* src_buffer, TextureVulkanImage* dst_image,
const Vector<BufferImageCopy>& regions)
{
EXIT_IF(src_buffer == nullptr);
EXIT_IF(src_buffer->buffer == nullptr);
EXIT_IF(dst_image == nullptr);
EXIT_IF(dst_image->image == nullptr);
auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()];
EXIT_NOT_IMPLEMENTED(regions.Size() >= 16);
VkBufferImageCopy region[16];
uint32_t index = 0;
for (const auto& r: regions)
{
region[index].bufferOffset = r.offset;
region[index].bufferRowLength = 0;
region[index].bufferImageHeight = 0;
region[index].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region[index].imageSubresource.mipLevel = index;
region[index].imageSubresource.baseArrayLayer = 0;
region[index].imageSubresource.layerCount = 1;
region[index].imageOffset = {0, 0, 0};
region[index].imageExtent = {r.width, r.height, 1};
index++;
}
set_image_layout(vk_buffer, dst_image->image, index, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkCmdCopyBufferToImage(vk_buffer, src_buffer->buffer, dst_image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, index, region);
set_image_layout(vk_buffer, dst_image->image, index, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
void UtilBlitImage(CommandBuffer* buffer, VideoOutVulkanImage* src_image, VulkanSwapchain* dst_swapchain)
{
EXIT_IF(src_image == nullptr);
EXIT_IF(src_image->image == nullptr);
EXIT_IF(dst_swapchain == nullptr);
auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()];
auto* blt_dst_image = dst_swapchain->swapchain_images[dst_swapchain->current_index];
set_image_layout(vk_buffer, src_image->image, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
set_image_layout(vk_buffer, blt_dst_image, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkImageBlit region {};
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.srcOffsets[0].x = 0;
region.srcOffsets[0].y = 0;
region.srcOffsets[0].z = 0;
region.srcOffsets[1].x = static_cast<int>(src_image->extent.width);
region.srcOffsets[1].y = static_cast<int>(src_image->extent.height);
region.srcOffsets[1].z = 1;
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = 0;
region.dstSubresource.baseArrayLayer = 0;
region.dstSubresource.layerCount = 1;
region.dstOffsets[0].x = 0;
region.dstOffsets[0].y = 0;
region.dstOffsets[0].z = 0;
region.dstOffsets[1].x = static_cast<int>(dst_swapchain->swapchain_extent.width);
region.dstOffsets[1].y = static_cast<int>(dst_swapchain->swapchain_extent.height);
region.dstOffsets[1].z = 1;
vkCmdBlitImage(vk_buffer, src_image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, blt_dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &region, VK_FILTER_LINEAR);
set_image_layout(vk_buffer, src_image->image, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
}
void VulkanCreateBuffer(GraphicContext* gctx, uint64_t size, VulkanBuffer* buffer)
{
EXIT_IF(gctx == nullptr);
EXIT_IF(buffer == nullptr);
EXIT_IF(buffer->buffer != nullptr);
VkBufferCreateInfo buffer_info {};
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.size = size;
buffer_info.usage = buffer->usage;
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vkCreateBuffer(gctx->device, &buffer_info, nullptr, &buffer->buffer);
EXIT_NOT_IMPLEMENTED(buffer->buffer == nullptr);
vkGetBufferMemoryRequirements(gctx->device, buffer->buffer, &buffer->memory.requirements);
bool allocated = VulkanAllocate(gctx, &buffer->memory);
EXIT_NOT_IMPLEMENTED(!allocated);
// vkBindBufferMemory(gctx->device, buffer->buffer, buffer->memory.memory, buffer->memory.offset);
VulkanBindBufferMemory(gctx, buffer, &buffer->memory);
}
void VulkanDeleteBuffer(GraphicContext* gctx, VulkanBuffer* buffer)
{
EXIT_IF(buffer == nullptr);
EXIT_IF(gctx == nullptr);
DeleteDescriptor(buffer);
vkDestroyBuffer(gctx->device, buffer->buffer, nullptr);
VulkanFree(gctx, &buffer->memory);
buffer->buffer = nullptr;
}
void UtilFillImage(GraphicContext* ctx, VideoOutVulkanImage* image, const void* src_data, uint64_t size)
{
KYTY_PROFILER_FUNCTION();
EXIT_IF(ctx == nullptr);
EXIT_IF(image == nullptr);
VulkanBuffer staging_buffer {};
staging_buffer.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
staging_buffer.memory.property = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
VulkanCreateBuffer(ctx, size, &staging_buffer);
void* data = nullptr;
// vkMapMemory(ctx->device, staging_buffer.memory.memory, staging_buffer.memory.offset, size, 0, &data);
VulkanMapMemory(ctx, &staging_buffer.memory, &data);
std::memcpy(data, src_data, size);
// vkUnmapMemory(ctx->device, staging_buffer.memory.memory);
VulkanUnmapMemory(ctx, &staging_buffer.memory);
CommandBuffer buffer;
buffer.SetQueue(GraphicContext::QUEUE_UTIL);
EXIT_NOT_IMPLEMENTED(buffer.IsInvalid());
buffer.Begin();
UtilBufferToImage(&buffer, &staging_buffer, image);
buffer.End();
buffer.Execute();
buffer.WaitForFence();
VulkanDeleteBuffer(ctx, &staging_buffer);
}
void UtilSetImageLayoutOptimal(DepthStencilVulkanImage* image)
{
CommandBuffer buffer;
buffer.SetQueue(GraphicContext::QUEUE_UTIL);
EXIT_NOT_IMPLEMENTED(buffer.IsInvalid());
buffer.Begin();
auto* vk_buffer = buffer.GetPool()->buffers[buffer.GetIndex()];
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (image->format == VK_FORMAT_D24_UNORM_S8_UINT || image->format == VK_FORMAT_D32_SFLOAT_S8_UINT)
{
aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
set_image_layout(vk_buffer, image->image, 1, aspect_mask, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
buffer.End();
buffer.Execute();
buffer.WaitForFence();
}
void UtilSetImageLayoutOptimal(VideoOutVulkanImage* image)
{
CommandBuffer buffer;
buffer.SetQueue(GraphicContext::QUEUE_UTIL);
EXIT_NOT_IMPLEMENTED(buffer.IsInvalid());
buffer.Begin();
auto* vk_buffer = buffer.GetPool()->buffers[buffer.GetIndex()];
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
set_image_layout(vk_buffer, image->image, 1, aspect_mask, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
buffer.End();
buffer.Execute();
buffer.WaitForFence();
}
void UtilFillImage(GraphicContext* ctx, TextureVulkanImage* image, const void* src_data, uint64_t size,
const Vector<BufferImageCopy>& regions)
{
EXIT_IF(ctx == nullptr);
EXIT_IF(image == nullptr);
VulkanBuffer staging_buffer {};
staging_buffer.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
staging_buffer.memory.property = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
VulkanCreateBuffer(ctx, size, &staging_buffer);
void* data = nullptr;
VulkanMapMemory(ctx, &staging_buffer.memory, &data);
std::memcpy(data, src_data, size);
VulkanUnmapMemory(ctx, &staging_buffer.memory);
CommandBuffer buffer;
buffer.SetQueue(GraphicContext::QUEUE_UTIL);
EXIT_NOT_IMPLEMENTED(buffer.IsInvalid());
buffer.Begin();
UtilBufferToImage(&buffer, &staging_buffer, image, regions);
buffer.End();
buffer.Execute();
buffer.WaitForFence();
VulkanDeleteBuffer(ctx, &staging_buffer);
}
void UtilCopyBuffer(VulkanBuffer* src_buffer, VulkanBuffer* dst_buffer, uint64_t size)
{
EXIT_IF(src_buffer == nullptr);
EXIT_IF(src_buffer->buffer == nullptr);
EXIT_IF(dst_buffer == nullptr);
EXIT_IF(dst_buffer->buffer == nullptr);
CommandBuffer buffer;
buffer.SetQueue(GraphicContext::QUEUE_UTIL);
EXIT_NOT_IMPLEMENTED(buffer.IsInvalid());
auto* vk_buffer = buffer.GetPool()->buffers[buffer.GetIndex()];
buffer.Begin();
VkBufferCopy copy_region {};
copy_region.srcOffset = 0;
copy_region.dstOffset = 0;
copy_region.size = size;
vkCmdCopyBuffer(vk_buffer, src_buffer->buffer, dst_buffer->buffer, 1, &copy_region);
buffer.End();
buffer.Execute();
buffer.WaitForFence();
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED
@@ -0,0 +1,94 @@
#include "Emulator/Graphics/VertexBuffer.h"
#include "Kyty/Core/DbgAssert.h"
#include "Emulator/Graphics/GraphicContext.h"
#include "Emulator/Graphics/Utils.h"
#include "Emulator/Profiler.h"
#include <vulkan/vulkan_core.h>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
void* VertexBufferGpuObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num,
VulkanMemory* mem) const
{
KYTY_PROFILER_BLOCK("VertexBufferGpuObject::Create");
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr || *vaddr == 0);
EXIT_IF(mem == nullptr);
EXIT_IF(ctx == nullptr);
auto* vk_obj = new VulkanBuffer;
vk_obj->usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
vk_obj->memory.property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
vk_obj->buffer = nullptr;
VulkanCreateBuffer(ctx, *size, vk_obj);
EXIT_NOT_IMPLEMENTED(vk_obj->buffer == nullptr);
VulkanBuffer staging_buffer {};
staging_buffer.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
staging_buffer.memory.property = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
VulkanCreateBuffer(ctx, *size, &staging_buffer);
EXIT_NOT_IMPLEMENTED(staging_buffer.buffer == nullptr);
void* data = nullptr;
// vkMapMemory(ctx->device, staging_buffer.memory.memory, staging_buffer.memory.offset, *size, 0, &data);
VulkanMapMemory(ctx, &staging_buffer.memory, &data);
memcpy(data, reinterpret_cast<void*>(*vaddr), *size);
// vkUnmapMemory(ctx->device, staging_buffer.memory.memory);
VulkanUnmapMemory(ctx, &staging_buffer.memory);
UtilCopyBuffer(&staging_buffer, vk_obj, *size);
VulkanDeleteBuffer(ctx, &staging_buffer);
return vk_obj;
}
static void update_func(GraphicContext* /*ctx*/, const uint64_t* /*params*/, void* /*obj*/, const uint64_t* /*vaddr*/,
const uint64_t* /*size*/, int /*vaddr_num*/)
{
KYTY_PROFILER_BLOCK("VertexBufferGpuObject::update_func");
KYTY_NOT_IMPLEMENTED;
}
bool VertexBufferGpuObject::Equal(const uint64_t* /*other*/) const
{
return true;
}
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* /*mem*/)
{
KYTY_PROFILER_BLOCK("VertexBufferGpuObject::delete_func");
auto* vk_obj = reinterpret_cast<VulkanBuffer*>(obj);
EXIT_IF(vk_obj == nullptr);
EXIT_IF(vk_obj->buffer == nullptr);
EXIT_IF(ctx == nullptr);
VulkanDeleteBuffer(ctx, vk_obj);
delete vk_obj;
}
GpuObject::delete_func_t VertexBufferGpuObject::GetDeleteFunc() const
{
return delete_func;
}
GpuObject::update_func_t VertexBufferGpuObject::GetUpdateFunc() const
{
return update_func;
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED
+722
View File
@@ -0,0 +1,722 @@
#include "Emulator/Graphics/VideoOut.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/LinkList.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Threads.h"
#include "Emulator/Common.h"
#include "Emulator/Config.h"
#include "Emulator/Graphics/GpuMemory.h"
#include "Emulator/Graphics/GraphicsRender.h"
#include "Emulator/Graphics/Tile.h"
#include "Emulator/Graphics/VideoOutBuffer.h"
#include "Emulator/Graphics/Window.h"
#include "Emulator/Kernel/Pthread.h"
#include "Emulator/Libs/Errno.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/Profiler.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
struct GraphicContext;
} // namespace Kyty::Libs::Graphics
namespace Kyty::Libs::VideoOut {
LIB_NAME("VideoOut", "VideoOut");
namespace EventQueue = LibKernel::EventQueue;
constexpr int VIDEO_OUT_EVENT_FLIP = 0;
struct VideoOutResolutionStatus
{
uint32_t fullWidth = 1280;
uint32_t fullHeight = 720;
uint32_t paneWidth = 1280;
uint32_t paneHeight = 720;
uint64_t refreshRate = 3;
float screenSizeInInch = 50;
uint16_t flags = 0;
uint16_t reserved0 = 0;
uint32_t reserved1[3] = {0};
};
struct VideoOutBufferAttribute
{
uint32_t pixelFormat;
uint32_t tilingMode;
uint32_t aspectRatio;
uint32_t width;
uint32_t height;
uint32_t pitchInPixel;
uint32_t option;
uint32_t reserved0;
uint64_t reserved1;
};
struct VideoOutFlipStatus
{
uint64_t count = 0;
uint64_t processTime = 0;
uint64_t tsc = 0;
int64_t flipArg = 0;
uint64_t submitTsc = 0;
uint64_t reserved0 = 0;
int32_t gcQueueNum = 0;
int32_t flipPendingNum = 0;
int32_t currentBuffer = 0;
uint32_t reserved1 = 0;
};
struct VideoOutBufferSet
{
VideoOutBufferAttribute attr = {};
int start_index = 0;
int num = 0;
};
struct VideoOutBufferInfo
{
void* buffer = nullptr;
Graphics::VideoOutVulkanImage* buffer_vulkan = nullptr;
uint64_t buffer_size = 0;
int set_id = 0;
};
struct VideoOutConfig
{
VideoOutResolutionStatus resolution;
bool opened = false;
int flip_rate = 0;
EventQueue::KernelEqueue flip_eq = nullptr;
VideoOutFlipStatus flip_status;
VideoOutBufferInfo buffers[16];
VideoOutBufferSet buffers_sets[16];
int buffers_sets_num = 0;
};
class FlipQueue
{
public:
FlipQueue() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
virtual ~FlipQueue() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(FlipQueue);
bool Submit(VideoOutConfig* cfg, int index, int64_t flip_arg);
bool Flip(uint32_t micros);
void GetFlipStatus(VideoOutConfig* cfg, VideoOutFlipStatus* out);
void Wait(VideoOutConfig* cfg, int index);
private:
struct Request
{
VideoOutConfig* cfg;
int index;
int64_t flip_arg;
uint64_t submit_tsc;
};
Core::Mutex m_mutex;
Core::CondVar m_submit_cond_var;
Core::CondVar m_done_cond_var;
Core::List<Request> m_requests;
};
class VideoOutContext
{
public:
static constexpr int VIDEO_OUT_NUM_MAX = 2;
VideoOutContext() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
virtual ~VideoOutContext() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(VideoOutContext);
int Open();
void Close(int handle);
VideoOutConfig* Get(int handle);
VideoOutBufferImageInfo FindImage(void* buffer);
void Init(uint32_t width, uint32_t height);
Graphics::GraphicContext* GetGraphicCtx()
{
Core::LockGuard lock(m_mutex);
if (m_graphic_ctx == nullptr)
{
m_graphic_ctx = Graphics::WindowGetGraphicContext();
}
return m_graphic_ctx;
}
FlipQueue& GetFlipQueue() { return m_flip_queue; }
private:
Core::Mutex m_mutex;
VideoOutConfig m_video_out_ctx[VIDEO_OUT_NUM_MAX];
Graphics::GraphicContext* m_graphic_ctx = nullptr;
FlipQueue m_flip_queue;
};
static VideoOutContext* g_video_out_context = nullptr;
static uint64_t calc_buffer_size(const VideoOutBufferAttribute* attribute)
{
bool tile = attribute->tilingMode == 0;
bool neo = Config::IsNeo();
uint32_t width = attribute->width;
uint32_t height = attribute->height;
EXIT_NOT_IMPLEMENTED(attribute->width != attribute->pitchInPixel);
EXIT_NOT_IMPLEMENTED(attribute->option != 0);
EXIT_NOT_IMPLEMENTED(attribute->aspectRatio != 0);
EXIT_NOT_IMPLEMENTED(attribute->pixelFormat != 0x80000000);
uint32_t size = 0;
Graphics::TileGetVideoOutSize(width, height, tile, neo, &size);
return size;
}
void VideoOutInit(uint32_t width, uint32_t height)
{
EXIT_IF(g_video_out_context != nullptr);
g_video_out_context = new VideoOutContext;
g_video_out_context->Init(width, height);
}
void VideoOutContext::Init(uint32_t width, uint32_t height)
{
for (auto& ctx: m_video_out_ctx)
{
ctx.resolution.fullWidth = width;
ctx.resolution.fullHeight = height;
ctx.resolution.paneWidth = width;
ctx.resolution.paneHeight = height;
}
}
int VideoOutContext::Open()
{
Core::LockGuard lock(m_mutex);
int handle = -1;
for (int i = 1; i < VIDEO_OUT_NUM_MAX; i++)
{
if (!m_video_out_ctx[i].opened)
{
handle = i;
break;
}
}
EXIT_IF(m_video_out_ctx[handle].flip_eq != nullptr);
EXIT_IF(m_video_out_ctx[handle].flip_rate != 0);
m_video_out_ctx[handle].opened = true;
m_video_out_ctx[handle].flip_status = VideoOutFlipStatus();
m_video_out_ctx[handle].flip_status.flipArg = -1;
m_video_out_ctx[handle].flip_status.currentBuffer = -1;
m_video_out_ctx[handle].flip_status.count = 0;
return handle;
}
void VideoOutContext::Close(int handle)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(handle >= VIDEO_OUT_NUM_MAX);
EXIT_NOT_IMPLEMENTED(!m_video_out_ctx[handle].opened);
m_video_out_ctx[handle].opened = false;
if (m_video_out_ctx[handle].flip_eq != nullptr)
{
EventQueue::KernelDeleteEvent(m_video_out_ctx[handle].flip_eq, VIDEO_OUT_EVENT_FLIP, EventQueue::KERNEL_EVFILT_VIDEO_OUT);
EXIT_IF(m_video_out_ctx[handle].flip_eq != nullptr);
}
m_video_out_ctx[handle].flip_rate = 0;
for (int i = 0; i < 16; i++)
{
m_video_out_ctx[handle].buffers[i].buffer = nullptr;
m_video_out_ctx[handle].buffers[i].buffer_vulkan = nullptr;
m_video_out_ctx[handle].buffers[i].buffer_size = 0;
m_video_out_ctx[handle].buffers[i].set_id = 0;
m_video_out_ctx[handle].buffers_sets[i].num = 0;
m_video_out_ctx[handle].buffers_sets[i].start_index = 0;
}
m_video_out_ctx[handle].buffers_sets_num = 0;
}
VideoOutConfig* VideoOutContext::Get(int handle)
{
EXIT_NOT_IMPLEMENTED(handle >= VIDEO_OUT_NUM_MAX);
EXIT_NOT_IMPLEMENTED(!m_video_out_ctx[handle].opened);
return m_video_out_ctx + handle;
}
VideoOutBufferImageInfo VideoOutContext::FindImage(void* buffer)
{
VideoOutBufferImageInfo ret;
Core::LockGuard lock(m_mutex);
for (auto& ctx: m_video_out_ctx)
{
if (ctx.opened)
{
for (int i = 0; i < ctx.buffers_sets_num; i++)
{
for (int j = ctx.buffers_sets[i].start_index; j < ctx.buffers_sets[i].num; j++)
{
if (ctx.buffers[j].buffer == buffer)
{
ret.image = ctx.buffers[j].buffer_vulkan;
ret.buffer_size = ctx.buffers[j].buffer_size;
ret.index = j - ctx.buffers_sets[i].start_index;
goto END;
}
}
}
}
}
END:
return ret;
}
bool FlipQueue::Submit(VideoOutConfig* cfg, int index, int64_t flip_arg)
{
Core::LockGuard lock(m_mutex);
if (m_requests.Size() >= 2)
{
return false;
}
Request r {};
r.cfg = cfg;
r.index = index;
r.flip_arg = flip_arg;
r.submit_tsc = LibKernel::KernelReadTsc();
m_requests.Add(r);
cfg->flip_status.flipPendingNum = static_cast<int>(m_requests.Size());
cfg->flip_status.gcQueueNum = 0;
m_submit_cond_var.Signal();
return true;
}
void FlipQueue::Wait(VideoOutConfig* cfg, int index)
{
Core::LockGuard lock(m_mutex);
while (
m_requests.IndexValid(m_requests.Find(cfg, index, [](auto r, auto cfg, auto index) { return r.cfg == cfg && r.index == index; })))
{
m_done_cond_var.Wait(&m_mutex);
}
}
bool FlipQueue::Flip(uint32_t micros)
{
KYTY_PROFILER_BLOCK("FlipQueue::Flip");
m_mutex.Lock();
if (m_requests.Size() == 0)
{
m_submit_cond_var.WaitFor(&m_mutex, micros);
if (m_requests.Size() == 0)
{
m_mutex.Unlock();
return false;
}
}
auto first = m_requests.First();
auto r = m_requests.At(first);
m_mutex.Unlock();
auto* buffer = r.cfg->buffers[r.index].buffer_vulkan;
// if (buffer->framebuffer == nullptr)
// {
// // TODO(): Flush via GpuMemoryFlush()
// const auto& attribute = r.cfg->buffers_sets[r.cfg->buffers[r.index].set_id].attr;
// auto buffer_size = calc_buffer_size(&attribute);
// EXIT_NOT_IMPLEMENTED(buffer_size == 0);
// Graphics::VideoOutBufferObject vulkan_buffer_info(attribute.pixelFormat, attribute.width, attribute.height,
// (attribute.tilingMode == 0), Config::IsNeo());
// r.cfg->buffers[r.index].buffer_vulkan = static_cast<Graphics::VideoOutVulkanImage*>(
// Graphics::GpuMemoryGetObject(g_video_out_context->GetGraphicCtx(),
// reinterpret_cast<uint64_t>(r.cfg->buffers[r.index].buffer), buffer_size, vulkan_buffer_info));
// EXIT_NOT_IMPLEMENTED(r.cfg->buffers[r.index].buffer_vulkan != buffer);
// }
Graphics::WindowDrawBuffer(buffer);
if (r.cfg->flip_eq != nullptr)
{
auto result = EventQueue::KernelTriggerEvent(r.cfg->flip_eq, VIDEO_OUT_EVENT_FLIP, EventQueue::KERNEL_EVFILT_VIDEO_OUT,
reinterpret_cast<void*>(r.flip_arg));
EXIT_NOT_IMPLEMENTED(result != OK);
}
printf("Flip done: %d\n", r.index);
m_mutex.Lock();
m_requests.Remove(first);
m_done_cond_var.Signal();
r.cfg->flip_status.count++;
r.cfg->flip_status.processTime = LibKernel::KernelGetProcessTime();
r.cfg->flip_status.tsc = LibKernel::KernelReadTsc();
r.cfg->flip_status.submitTsc = r.submit_tsc;
r.cfg->flip_status.flipArg = r.flip_arg;
r.cfg->flip_status.currentBuffer = r.index;
r.cfg->flip_status.flipPendingNum = static_cast<int>(m_requests.Size());
m_mutex.Unlock();
Graphics::GpuMemoryFrameDone();
Graphics::GpuMemoryDbgDump();
return true;
}
void FlipQueue::GetFlipStatus(VideoOutConfig* cfg, VideoOutFlipStatus* out)
{
EXIT_IF(cfg == nullptr);
EXIT_IF(out == nullptr);
Core::LockGuard lock(m_mutex);
*out = cfg->flip_status;
}
bool FlipWindow(uint32_t micros)
{
EXIT_IF(g_video_out_context == nullptr);
return g_video_out_context->GetFlipQueue().Flip(micros);
}
KYTY_SYSV_ABI int VideoOutOpen(int user_id, int bus_type, int index, const void* param)
{
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
EXIT_NOT_IMPLEMENTED(user_id != 255 && user_id != 0);
EXIT_NOT_IMPLEMENTED(bus_type != 0);
EXIT_NOT_IMPLEMENTED(index != 0);
EXIT_NOT_IMPLEMENTED(param != nullptr);
int handle = g_video_out_context->Open();
if (handle < 0)
{
return VIDEO_OUT_ERROR_RESOURCE_BUSY;
}
return handle;
}
KYTY_SYSV_ABI int VideoOutClose(int handle)
{
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
g_video_out_context->Close(handle);
return OK;
}
KYTY_SYSV_ABI int VideoOutGetResolutionStatus(int handle, VideoOutResolutionStatus* status)
{
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
EXIT_NOT_IMPLEMENTED(status == nullptr);
*status = g_video_out_context->Get(handle)->resolution;
return OK;
}
KYTY_SYSV_ABI void VideoOutSetBufferAttribute(VideoOutBufferAttribute* attribute, uint32_t pixel_format, uint32_t tiling_mode,
uint32_t aspect_ratio, uint32_t width, uint32_t height, uint32_t pitch_in_pixel)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(attribute == nullptr);
printf("\tpixel_format = %08" PRIx32 "\n", pixel_format);
printf("\ttiling_mode = %" PRIu32 "\n", tiling_mode);
printf("\taspect_ratio = %" PRIu32 "\n", aspect_ratio);
printf("\twidth = %" PRIu32 "\n", width);
printf("\theight = %" PRIu32 "\n", height);
printf("\tpitch_in_pixel = %" PRIu32 "\n", pitch_in_pixel);
memset(attribute, 0, sizeof(VideoOutBufferAttribute));
attribute->pixelFormat = pixel_format;
attribute->tilingMode = tiling_mode;
attribute->aspectRatio = aspect_ratio;
attribute->width = width;
attribute->height = height;
attribute->pitchInPixel = pitch_in_pixel;
}
KYTY_SYSV_ABI int VideoOutSetFlipRate(int handle, int rate)
{
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
EXIT_NOT_IMPLEMENTED(rate < 0 || rate > 2);
printf("\trate = %d\n", rate);
g_video_out_context->Get(handle)->flip_rate = rate;
return OK;
}
static void flip_event_reset_func(LibKernel::EventQueue::KernelEqueueEvent* event)
{
EXIT_IF(event == nullptr);
event->triggered = false;
event->event.fflags = 0;
event->event.data = 0;
}
static void flip_event_delete_func(LibKernel::EventQueue::KernelEqueueEvent* event)
{
EXIT_IF(event == nullptr);
EXIT_IF(event->filter.data == nullptr);
if (event->filter.data != nullptr)
{
auto* video_out = static_cast<VideoOutConfig*>(event->filter.data);
EXIT_IF(video_out->flip_eq == nullptr);
video_out->flip_eq = nullptr;
}
}
static void flip_event_trigger_func(LibKernel::EventQueue::KernelEqueueEvent* event, void* trigger_data)
{
EXIT_IF(event == nullptr);
event->triggered = true;
event->event.fflags++;
event->event.data = reinterpret_cast<intptr_t>(trigger_data);
}
KYTY_SYSV_ABI int VideoOutAddFlipEvent(EventQueue::KernelEqueue eq, int handle, void* udata)
{
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
auto* ctx = g_video_out_context->Get(handle);
EXIT_NOT_IMPLEMENTED(ctx->flip_eq != nullptr);
if (eq == nullptr)
{
return VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
}
EventQueue::KernelEqueueEvent event;
event.triggered = false;
event.event.ident = VIDEO_OUT_EVENT_FLIP;
event.event.filter = EventQueue::KERNEL_EVFILT_VIDEO_OUT;
event.event.udata = udata;
event.event.fflags = 0;
event.event.data = 0;
event.filter.delete_func = flip_event_delete_func;
event.filter.reset_func = flip_event_reset_func;
event.filter.trigger_func = flip_event_trigger_func;
event.filter.data = ctx;
int result = EventQueue::KernelAddEvent(eq, event);
ctx->flip_eq = eq;
return result;
}
KYTY_SYSV_ABI int VideoOutRegisterBuffers(int handle, int start_index, void* const* addresses, int buffer_num,
const VideoOutBufferAttribute* attribute)
{
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
auto* ctx = g_video_out_context->Get(handle);
if (addresses == nullptr)
{
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
if (attribute == nullptr)
{
return VIDEO_OUT_ERROR_INVALID_OPTION;
}
if (start_index < 0 || start_index > 15 || buffer_num < 1 || buffer_num > 16 || start_index + buffer_num > 15)
{
return VIDEO_OUT_ERROR_INVALID_VALUE;
}
Graphics::WindowWaitForGraphicInitialized();
Graphics::GraphicsRenderCreateContext();
int set_index = ctx->buffers_sets_num++;
if (set_index > 15)
{
return VIDEO_OUT_ERROR_NO_EMPTY_SLOT;
}
printf("\tstart_index = %d\n", start_index);
printf("\tbuffer_num = %d\n", buffer_num);
printf("\tpixel_format = 0x%08" PRIx32 "\n", attribute->pixelFormat);
printf("\ttiling_mode = %" PRIu32 "\n", attribute->tilingMode);
printf("\taspect_ratio = %" PRIu32 "\n", attribute->aspectRatio);
printf("\twidth = %" PRIu32 "\n", attribute->width);
printf("\theight = %" PRIu32 "\n", attribute->height);
printf("\tpitch_in_pixel = %" PRIu32 "\n", attribute->pitchInPixel);
printf("\toption = %" PRIu32 "\n", attribute->option);
EXIT_NOT_IMPLEMENTED(attribute->pixelFormat != 0x80000000);
EXIT_NOT_IMPLEMENTED(attribute->tilingMode != 0);
EXIT_NOT_IMPLEMENTED(attribute->aspectRatio != 0);
EXIT_NOT_IMPLEMENTED(attribute->pitchInPixel != attribute->width);
EXIT_NOT_IMPLEMENTED(attribute->option != 0);
auto buffer_size = calc_buffer_size(attribute);
EXIT_NOT_IMPLEMENTED(buffer_size == 0);
ctx->buffers_sets[set_index].start_index = start_index;
ctx->buffers_sets[set_index].num = buffer_num;
ctx->buffers_sets[set_index].attr = *attribute;
Graphics::VideoOutBufferObject vulkan_buffer_info(attribute->pixelFormat, attribute->width, attribute->height,
(attribute->tilingMode == 0), Config::IsNeo());
for (int i = 0; i < buffer_num; i++)
{
if (ctx->buffers[i + start_index].buffer != nullptr)
{
return VIDEO_OUT_ERROR_SLOT_OCCUPIED;
}
ctx->buffers[i + start_index].set_id = set_index;
ctx->buffers[i + start_index].buffer = addresses[i];
ctx->buffers[i + start_index].buffer_size = buffer_size;
ctx->buffers[i + start_index].buffer_vulkan = static_cast<Graphics::VideoOutVulkanImage*>(Graphics::GpuMemoryGetObject(
g_video_out_context->GetGraphicCtx(), reinterpret_cast<uint64_t>(addresses[i]), buffer_size, vulkan_buffer_info));
EXIT_NOT_IMPLEMENTED(ctx->buffers[i + start_index].buffer_vulkan == nullptr);
printf("\tbuffers[%d] = %016" PRIx64 "\n", i + start_index, reinterpret_cast<uint64_t>(addresses[i]));
}
// Graphics::GpuMemoryDbgDump();
return set_index;
}
VideoOutBufferImageInfo VideoOutGetImage(uint64_t addr)
{
EXIT_IF(g_video_out_context == nullptr);
return g_video_out_context->FindImage(reinterpret_cast<void*>(addr));
}
KYTY_SYSV_ABI int VideoOutSubmitFlip(int handle, int index, int flip_mode, int64_t flip_arg)
{
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
auto* ctx = g_video_out_context->Get(handle);
EXIT_NOT_IMPLEMENTED(flip_mode != 1);
if (index < 0 || index > 15)
{
return VIDEO_OUT_ERROR_INVALID_INDEX;
}
if (!g_video_out_context->GetFlipQueue().Submit(ctx, index, flip_arg))
{
return VIDEO_OUT_ERROR_FLIP_QUEUE_FULL;
}
return OK;
}
void VideoOutWaitFlipDone(int handle, int index)
{
EXIT_IF(g_video_out_context == nullptr);
auto* ctx = g_video_out_context->Get(handle);
EXIT_NOT_IMPLEMENTED(index < 0 || index > 15);
g_video_out_context->GetFlipQueue().Wait(ctx, index);
}
KYTY_SYSV_ABI int VideoOutGetFlipStatus(int handle, VideoOutFlipStatus* status)
{
PRINT_NAME();
EXIT_IF(g_video_out_context == nullptr);
if (status == nullptr)
{
return VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
auto* ctx = g_video_out_context->Get(handle);
g_video_out_context->GetFlipQueue().GetFlipStatus(ctx, status);
printf("\t count = %" PRIu64 "\n", status->count);
printf("\t processTime = %" PRIu64 "\n", status->processTime);
printf("\t tsc = %" PRIu64 "\n", status->tsc);
printf("\t submitTsc = %" PRIu64 "\n", status->submitTsc);
printf("\t flipArg = %" PRId64 "\n", status->flipArg);
printf("\t gcQueueNum = %d\n", status->gcQueueNum);
printf("\t flipPendingNum = %d\n", status->flipPendingNum);
printf("\t currentBuffer = %d\n", status->currentBuffer);
return OK;
}
} // namespace Kyty::Libs::VideoOut
#endif // KYTY_EMU_ENABLED
@@ -0,0 +1,190 @@
#include "Emulator/Graphics/VideoOutBuffer.h"
#include "Kyty/Core/DbgAssert.h"
#include "Emulator/Graphics/GraphicContext.h"
#include "Emulator/Graphics/GraphicsRender.h"
#include "Emulator/Graphics/Tile.h"
#include "Emulator/Graphics/Utils.h"
#include "Emulator/Profiler.h"
#include <vulkan/vulkan_core.h>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
void* VideoOutBufferObject::Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const
{
KYTY_PROFILER_BLOCK("VideoOutBufferObject::Create");
EXIT_IF(vaddr_num != 1 || size == nullptr || vaddr == nullptr);
EXIT_IF(mem == nullptr);
EXIT_IF(ctx == nullptr);
auto pixel_format = params[PARAM_FORMAT];
auto width = params[PARAM_WIDTH];
auto height = params[PARAM_HEIGHT];
EXIT_NOT_IMPLEMENTED(pixel_format != 0x80000000);
EXIT_NOT_IMPLEMENTED(width == 0);
EXIT_NOT_IMPLEMENTED(height == 0);
auto* vk_obj = new VideoOutVulkanImage;
vk_obj->extent.width = width;
vk_obj->extent.height = height;
vk_obj->format = VK_FORMAT_R8G8B8A8_SRGB;
vk_obj->image = nullptr;
vk_obj->image_view = nullptr;
VkImageCreateInfo image_info {};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.pNext = nullptr;
image_info.flags = 0;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.extent.width = vk_obj->extent.width;
image_info.extent.height = vk_obj->extent.height;
image_info.extent.depth = 1;
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.format = vk_obj->format;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = static_cast<VkImageUsageFlags>(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT) |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
vkCreateImage(ctx->device, &image_info, nullptr, &vk_obj->image);
EXIT_NOT_IMPLEMENTED(vk_obj->image == nullptr);
vkGetImageMemoryRequirements(ctx->device, vk_obj->image, &mem->requirements);
mem->property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
bool allocated = VulkanAllocate(ctx, mem);
EXIT_NOT_IMPLEMENTED(!allocated);
// vkBindImageMemory(ctx->device, vk_obj->image, mem->memory, mem->offset);
VulkanBindImageMemory(ctx, vk_obj, mem);
vk_obj->memory = *mem;
EXIT_NOT_IMPLEMENTED(mem->requirements.size > *size);
GetUpdateFunc()(ctx, params, vk_obj, vaddr, size, vaddr_num);
VkImageViewCreateInfo create_info {};
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
create_info.pNext = nullptr;
create_info.flags = 0;
create_info.image = vk_obj->image;
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
create_info.format = vk_obj->format;
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
create_info.subresourceRange.baseArrayLayer = 0;
create_info.subresourceRange.baseMipLevel = 0;
create_info.subresourceRange.layerCount = 1;
create_info.subresourceRange.levelCount = 1;
vkCreateImageView(ctx->device, &create_info, nullptr, &vk_obj->image_view);
EXIT_NOT_IMPLEMENTED(vk_obj->image_view == nullptr);
return vk_obj;
}
static bool buffer_is_tiled(uint64_t vaddr, uint64_t size)
{
if ((size & 0x7u) == 0)
{
const auto* ptr = reinterpret_cast<const uint64_t*>(vaddr);
const auto* ptr_end = reinterpret_cast<const uint64_t*>(vaddr + size / 8);
for (uint64_t element = *ptr; ptr < ptr_end; ptr++)
{
if (element != *ptr)
{
return true;
}
}
return false;
}
return true;
}
static void update_func(GraphicContext* ctx, const uint64_t* params, void* obj, const uint64_t* vaddr, const uint64_t* size, int vaddr_num)
{
KYTY_PROFILER_BLOCK("VideoOutBufferObject::update_func");
EXIT_IF(obj == nullptr);
EXIT_IF(ctx == nullptr);
EXIT_IF(params == nullptr);
EXIT_IF(vaddr == nullptr || size == nullptr || vaddr_num != 1);
auto* vk_obj = static_cast<VideoOutVulkanImage*>(obj);
bool tiled = (params[VideoOutBufferObject::PARAM_TILED] != 0);
bool neo = (params[VideoOutBufferObject::PARAM_NEO] != 0);
if (tiled && buffer_is_tiled(*vaddr, *size))
{
auto* temp_buf = new uint8_t[*size];
TileConvertTiledToLinear(temp_buf, reinterpret_cast<void*>(*vaddr), TileMode::VideoOutTiled,
params[VideoOutBufferObject::PARAM_WIDTH], params[VideoOutBufferObject::PARAM_HEIGHT], neo);
UtilFillImage(ctx, vk_obj, temp_buf, *size);
delete[] temp_buf;
} else
{
UtilFillImage(ctx, vk_obj, reinterpret_cast<void*>(*vaddr), *size);
}
}
bool VideoOutBufferObject::Equal(const uint64_t* other) const
{
return (params[PARAM_FORMAT] == other[PARAM_FORMAT] && params[PARAM_WIDTH] == other[PARAM_WIDTH] &&
params[PARAM_HEIGHT] == other[PARAM_HEIGHT] && params[PARAM_TILED] == other[PARAM_TILED]);
}
static void delete_func(GraphicContext* ctx, void* obj, VulkanMemory* mem)
{
KYTY_PROFILER_BLOCK("VideoOutBufferObject::delete_func");
auto* vk_obj = reinterpret_cast<VideoOutVulkanImage*>(obj);
EXIT_IF(vk_obj == nullptr);
EXIT_IF(ctx == nullptr);
// if (vk_obj->framebuffer != nullptr)
{
DeleteFramebuffer(vk_obj);
}
vkDestroyImageView(ctx->device, vk_obj->image_view, nullptr);
vkDestroyImage(ctx->device, vk_obj->image, nullptr);
VulkanFree(ctx, mem);
delete vk_obj;
}
GpuObject::delete_func_t VideoOutBufferObject::GetDeleteFunc() const
{
return delete_func;
}
GpuObject::update_func_t VideoOutBufferObject::GetUpdateFunc() const
{
return update_func;
}
} // namespace Kyty::Libs::Graphics
#endif // KYTY_EMU_ENABLED
File diff suppressed because it is too large Load Diff
+444
View File
@@ -0,0 +1,444 @@
#include "Emulator/Kernel/EventFlag.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Threads.h"
#include "Kyty/Core/Timer.h"
#include "Emulator/Libs/Errno.h"
#include "Emulator/Libs/Libs.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::LibKernel::EventFlag {
LIB_NAME("libkernel", "libkernel");
class KernelEventFlagPrivate
{
public:
enum class Result
{
Ok,
AlreadyWaiting,
TimedOut,
Canceled,
Deleted
};
enum class ClearMode
{
None,
All,
Bits
};
enum class WaitMode
{
And,
Or
};
KernelEventFlagPrivate(const String& name, bool flag, uint64_t bits): m_name(name), m_single_thread(flag), m_bits(bits) {};
virtual ~KernelEventFlagPrivate();
KYTY_CLASS_NO_COPY(KernelEventFlagPrivate);
void Set(uint64_t bits);
void Clear(uint64_t bits);
void Cancel(uint64_t bits, int* num_waiting_threads);
Result Wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result, uint32_t* ptr_micros);
Result Poll(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result)
{
uint32_t micros = 0;
return Wait(bits, wait_mode, clear_mode, result, &micros);
}
private:
enum class Status
{
Set,
Canceled,
Deleted
};
Core::Mutex m_mutex;
Core::CondVar m_cond_var;
Status m_status = Status::Set;
int m_waiting_threads = 0;
String m_name;
bool m_single_thread = false;
uint64_t m_bits = 0;
};
KernelEventFlagPrivate::~KernelEventFlagPrivate()
{
Core::LockGuard lock(m_mutex);
while (m_status != Status::Set)
{
m_mutex.Unlock();
Core::Thread::SleepMicro(10);
m_mutex.Lock();
}
m_status = Status::Deleted;
m_cond_var.SignalAll();
while (m_waiting_threads > 0)
{
m_mutex.Unlock();
Core::Thread::SleepMicro(10);
m_mutex.Lock();
}
}
KernelEventFlagPrivate::Result KernelEventFlagPrivate::Wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result,
uint32_t* ptr_micros)
{
Core::LockGuard lock(m_mutex);
uint32_t micros = 0;
bool infinitely = true;
if (ptr_micros != nullptr)
{
micros = *ptr_micros;
infinitely = false;
}
uint32_t elapsed = 0;
Core::Timer t;
t.Start();
if (m_single_thread && m_waiting_threads > 0)
{
return Result::AlreadyWaiting;
}
while (!((wait_mode == WaitMode::And && (m_bits & bits) == bits) || (wait_mode == WaitMode::Or && (m_bits & bits) != 0)))
{
if ((elapsed >= micros && !infinitely))
{
if (result != nullptr)
{
*result = m_bits;
}
*ptr_micros = 0;
return Result::TimedOut;
}
m_waiting_threads++;
if (infinitely)
{
m_cond_var.Wait(&m_mutex);
} else
{
m_cond_var.WaitFor(&m_mutex, micros - elapsed);
}
m_waiting_threads--;
elapsed = static_cast<uint32_t>(t.GetTimeS() * 1000000.0);
if (m_status == Status::Canceled)
{
if (result != nullptr)
{
*result = m_bits;
}
if (ptr_micros != nullptr)
{
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
}
return Result::Canceled;
}
if (m_status == Status::Deleted)
{
if (result != nullptr)
{
*result = m_bits;
}
if (ptr_micros != nullptr)
{
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
}
return Result::Deleted;
}
}
if (result != nullptr)
{
*result = m_bits;
}
if (clear_mode == ClearMode::All)
{
m_bits = 0;
} else if (clear_mode == ClearMode::Bits)
{
m_bits &= ~bits;
}
if (ptr_micros != nullptr)
{
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
}
return Result::Ok;
}
void KernelEventFlagPrivate::Set(uint64_t bits)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(m_status == Status::Deleted);
while (m_status != Status::Set)
{
m_mutex.Unlock();
Core::Thread::SleepMicro(10);
m_mutex.Lock();
}
m_bits |= bits;
m_cond_var.SignalAll();
}
void KernelEventFlagPrivate::Clear(uint64_t bits)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(m_status == Status::Deleted);
while (m_status != Status::Set)
{
m_mutex.Unlock();
Core::Thread::SleepMicro(10);
m_mutex.Lock();
}
m_bits &= bits;
}
void KernelEventFlagPrivate::Cancel(uint64_t bits, int* num_waiting_threads)
{
Core::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(m_status == Status::Deleted);
while (m_status != Status::Set)
{
m_mutex.Unlock();
Core::Thread::SleepMicro(10);
m_mutex.Lock();
}
if (num_waiting_threads != nullptr)
{
*num_waiting_threads = m_waiting_threads;
}
m_status = Status::Canceled;
m_bits = bits;
m_cond_var.SignalAll();
while (m_waiting_threads > 0)
{
m_mutex.Unlock();
Core::Thread::SleepMicro(10);
m_mutex.Lock();
}
m_status = Status::Set;
}
int KYTY_SYSV_ABI KernelCreateEventFlag(KernelEventFlag* ef, const char* name, uint32_t attr, uint64_t init_pattern, const void* param)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(param != nullptr);
if (ef == nullptr || name == nullptr)
{
return KERNEL_ERROR_EINVAL;
}
bool single = false;
switch (attr)
{
case 0x10: single = true; break;
case 0x20: single = false; break;
default: EXIT("unknown attr: %u\n", attr);
}
*ef = new KernelEventFlagPrivate(String::FromUtf8(name), single, init_pattern);
printf("\tEventFlag create: %s\n", name);
return OK;
}
int KYTY_SYSV_ABI KernelDeleteEventFlag(KernelEventFlag ef)
{
PRINT_NAME();
if (ef == nullptr)
{
return KERNEL_ERROR_ESRCH;
}
delete ef;
return OK;
}
int KYTY_SYSV_ABI KernelWaitEventFlag(KernelEventFlag ef, uint64_t bit_pattern, uint32_t wait_mode, uint64_t* result_pat,
KernelUseconds* timeout)
{
PRINT_NAME();
if (ef == nullptr)
{
return KERNEL_ERROR_ESRCH;
}
if (bit_pattern == 0)
{
return KERNEL_ERROR_EINVAL;
}
KernelEventFlagPrivate::WaitMode wait = KernelEventFlagPrivate::WaitMode::And;
KernelEventFlagPrivate::ClearMode clear = KernelEventFlagPrivate::ClearMode::None;
switch (wait_mode & 0xfu)
{
case 0x01: wait = KernelEventFlagPrivate::WaitMode::And; break;
case 0x02: wait = KernelEventFlagPrivate::WaitMode::Or; break;
default: EXIT("unknown mode: %u\n", wait_mode);
}
switch (wait_mode & 0xf0u)
{
case 0x00: clear = KernelEventFlagPrivate::ClearMode::None; break;
case 0x10: clear = KernelEventFlagPrivate::ClearMode::All; break;
case 0x20: clear = KernelEventFlagPrivate::ClearMode::Bits; break;
default: EXIT("unknown mode: %u\n", wait_mode);
}
auto result = ef->Wait(bit_pattern, wait, clear, result_pat, timeout);
int ret = OK;
switch (result)
{
case KernelEventFlagPrivate::Result::Ok: ret = OK; break;
case KernelEventFlagPrivate::Result::AlreadyWaiting: ret = KERNEL_ERROR_EPERM; break;
case KernelEventFlagPrivate::Result::TimedOut: ret = KERNEL_ERROR_ETIMEDOUT; break;
case KernelEventFlagPrivate::Result::Canceled: ret = KERNEL_ERROR_ECANCELED; break;
case KernelEventFlagPrivate::Result::Deleted: ret = KERNEL_ERROR_EACCES; break;
}
return ret;
}
int KYTY_SYSV_ABI KernelPollEventFlag(KernelEventFlag ef, uint64_t bit_pattern, uint32_t wait_mode, uint64_t* result_pat)
{
PRINT_NAME();
if (ef == nullptr)
{
return KERNEL_ERROR_ESRCH;
}
if (bit_pattern == 0)
{
return KERNEL_ERROR_EINVAL;
}
KernelEventFlagPrivate::WaitMode wait = KernelEventFlagPrivate::WaitMode::And;
KernelEventFlagPrivate::ClearMode clear = KernelEventFlagPrivate::ClearMode::None;
switch (wait_mode & 0xfu)
{
case 0x01: wait = KernelEventFlagPrivate::WaitMode::And; break;
case 0x02: wait = KernelEventFlagPrivate::WaitMode::Or; break;
default: EXIT("unknown mode: %u\n", wait_mode);
}
switch (wait_mode & 0xf0u)
{
case 0x00: clear = KernelEventFlagPrivate::ClearMode::None; break;
case 0x10: clear = KernelEventFlagPrivate::ClearMode::All; break;
case 0x20: clear = KernelEventFlagPrivate::ClearMode::Bits; break;
default: EXIT("unknown mode: %u\n", wait_mode);
}
auto result = ef->Poll(bit_pattern, wait, clear, result_pat);
int ret = OK;
switch (result)
{
case KernelEventFlagPrivate::Result::Ok: ret = OK; break;
case KernelEventFlagPrivate::Result::AlreadyWaiting: ret = KERNEL_ERROR_EPERM; break;
case KernelEventFlagPrivate::Result::TimedOut:
case KernelEventFlagPrivate::Result::Canceled:
case KernelEventFlagPrivate::Result::Deleted: ret = KERNEL_ERROR_EBUSY; break;
}
return ret;
}
int KYTY_SYSV_ABI KernelSetEventFlag(KernelEventFlag ef, uint64_t bit_pattern)
{
PRINT_NAME();
if (ef == nullptr)
{
return KERNEL_ERROR_ESRCH;
}
ef->Set(bit_pattern);
return OK;
}
int KYTY_SYSV_ABI KernelClearEventFlag(KernelEventFlag ef, uint64_t bit_pattern)
{
PRINT_NAME();
if (ef == nullptr)
{
return KERNEL_ERROR_ESRCH;
}
ef->Clear(bit_pattern);
return OK;
}
int KYTY_SYSV_ABI KernelCancelEventFlag(KernelEventFlag ef, uint64_t set_pattern, int* num_wait_threads)
{
PRINT_NAME();
if (ef == nullptr)
{
return KERNEL_ERROR_ESRCH;
}
ef->Cancel(set_pattern, num_wait_threads);
return OK;
}
} // namespace Kyty::Libs::LibKernel::EventFlag
#endif // KYTY_EMU_ENABLED
+389
View File
@@ -0,0 +1,389 @@
#include "Emulator/Kernel/EventQueue.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/LinkList.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Threads.h"
#include "Kyty/Core/Timer.h"
#include "Emulator/Libs/Errno.h"
#include "Emulator/Libs/Libs.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::LibKernel::EventQueue {
LIB_NAME("libkernel", "libkernel");
class KernelEqueuePrivate
{
public:
KernelEqueuePrivate() = default;
virtual ~KernelEqueuePrivate();
KYTY_CLASS_NO_COPY(KernelEqueuePrivate);
[[nodiscard]] const String& GetName() const { return m_name; }
void SetName(const String& m_name) { this->m_name = m_name; }
void AddEvent(const KernelEqueueEvent& event);
bool TriggerEvent(uintptr_t ident, int16_t filter, void* trigger_data);
bool DeleteEvent(uintptr_t ident, int16_t filter);
int GetTriggeredEvents(KernelEvent* ev, int num);
int WaitForEvents(KernelEvent* ev, int num, uint32_t micros);
private:
Core::List<KernelEqueueEvent> m_events;
Core::Mutex m_mutex;
Core::CondVar m_cond_var;
String m_name;
};
KernelEqueuePrivate::~KernelEqueuePrivate()
{
Core::LockGuard lock(m_mutex);
FOR_LIST(index, m_events)
{
auto& event = m_events[index];
if (event.filter.delete_func != nullptr)
{
event.filter.delete_func(&event);
}
}
}
int KernelEqueuePrivate::GetTriggeredEvents(KernelEvent* ev, int num)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(num < 1);
int ret = 0;
FOR_LIST(index, m_events)
{
auto& event = m_events[index];
if (event.triggered)
{
ev[ret++] = event.event;
if (event.filter.reset_func != nullptr)
{
event.filter.reset_func(&event);
}
if (ret >= num)
{
break;
}
}
}
return ret;
}
int KernelEqueuePrivate::WaitForEvents(KernelEvent* ev, int num, uint32_t micros)
{
Core::LockGuard lock(m_mutex);
EXIT_IF(num < 1);
uint32_t elapsed = 0;
Core::Timer t;
t.Start();
for (;;)
{
int ret = GetTriggeredEvents(ev, num);
if (ret > 0 || (elapsed >= micros && micros != 0))
{
return ret;
}
if (micros == 0)
{
m_cond_var.Wait(&m_mutex);
} else
{
m_cond_var.WaitFor(&m_mutex, micros - elapsed);
}
elapsed = static_cast<uint32_t>(t.GetTimeS() * 1000000.0);
}
return 0;
}
void KernelEqueuePrivate::AddEvent(const KernelEqueueEvent& event)
{
Core::LockGuard lock(m_mutex);
if (auto index = m_events.Find(event.event.ident, event.event.filter,
[](auto e, auto ident, auto filter) { return e.event.ident == ident && e.event.filter == filter; });
m_events.IndexValid(index))
{
m_events[index] = event;
} else
{
m_events.Add(event);
}
if (event.triggered)
{
m_cond_var.Signal();
}
}
bool KernelEqueuePrivate::TriggerEvent(uintptr_t ident, int16_t filter, void* trigger_data)
{
Core::LockGuard lock(m_mutex);
if (auto index = m_events.Find(ident, filter,
[](auto e, auto ident, auto filter) { return e.event.ident == ident && e.event.filter == filter; });
m_events.IndexValid(index))
{
auto& event = m_events[index];
if (event.filter.trigger_func != nullptr)
{
event.filter.trigger_func(&event, trigger_data);
} else
{
event.triggered = true;
}
m_cond_var.Signal();
return true;
}
return false;
}
bool KernelEqueuePrivate::DeleteEvent(uintptr_t ident, int16_t filter)
{
Core::LockGuard lock(m_mutex);
if (auto index = m_events.Find(ident, filter,
[](auto e, auto ident, auto filter) { return e.event.ident == ident && e.event.filter == filter; });
m_events.IndexValid(index))
{
auto& event = m_events[index];
if (event.filter.delete_func != nullptr)
{
event.filter.delete_func(&event);
}
m_events.Remove(index);
return true;
}
return false;
}
int KYTY_SYSV_ABI KernelCreateEqueue(KernelEqueue* eq, const char* name)
{
PRINT_NAME();
if (eq == nullptr || name == nullptr)
{
return KERNEL_ERROR_EINVAL;
}
*eq = new KernelEqueuePrivate;
(*eq)->SetName(String::FromUtf8(name));
printf("\tEqueue create: %s\n", name);
return OK;
}
int KYTY_SYSV_ABI KernelAddEvent(KernelEqueue eq, const KernelEqueueEvent& event)
{
if (eq == nullptr)
{
return KERNEL_ERROR_EBADF;
}
eq->AddEvent(event);
return OK;
}
int KYTY_SYSV_ABI KernelTriggerEvent(KernelEqueue eq, uintptr_t ident, int16_t filter, void* trigger_data)
{
if (eq == nullptr)
{
return KERNEL_ERROR_EBADF;
}
if (!eq->TriggerEvent(ident, filter, trigger_data))
{
return KERNEL_ERROR_ENOENT;
}
return OK;
}
int KYTY_SYSV_ABI KernelDeleteEvent(KernelEqueue eq, uintptr_t ident, int16_t filter)
{
if (eq == nullptr)
{
return KERNEL_ERROR_EBADF;
}
if (!eq->DeleteEvent(ident, filter))
{
return KERNEL_ERROR_ENOENT;
}
return OK;
}
int KYTY_SYSV_ABI KernelDeleteEqueue(KernelEqueue eq)
{
PRINT_NAME();
if (eq == nullptr)
{
return KERNEL_ERROR_EBADF;
}
printf("\tEqueue delete: %s\n", eq->GetName().C_Str());
delete eq;
return OK;
}
int KYTY_SYSV_ABI KernelWaitEqueue(KernelEqueue eq, KernelEvent* ev, int num, int* out, const KernelUseconds* timo)
{
PRINT_NAME();
if (eq == nullptr)
{
return KERNEL_ERROR_EBADF;
}
if (ev == nullptr)
{
return KERNEL_ERROR_EFAULT;
}
if (num < 1)
{
return KERNEL_ERROR_EINVAL;
}
EXIT_NOT_IMPLEMENTED(out == nullptr);
printf("\tEqueue wait: %s\n", eq->GetName().C_Str());
if (timo == nullptr)
{
*out = eq->WaitForEvents(ev, num, 0);
}
if (timo != nullptr)
{
if (*timo == 0)
{
*out = eq->GetTriggeredEvents(ev, num);
} else
{
*out = eq->WaitForEvents(ev, num, *timo);
}
}
if (*out == 0)
{
printf("\ttimedout\n");
return KERNEL_ERROR_ETIMEDOUT;
}
printf("\treceived %u events\n", *out);
return OK;
}
intptr_t KYTY_SYSV_ABI KernelGetEventData(const KernelEvent* ev)
{
PRINT_NAME();
if (ev != nullptr)
{
return ev->data;
}
return 0;
}
intptr_t KYTY_SYSV_ABI KernelGetEventFflags(const KernelEvent* ev)
{
PRINT_NAME();
if (ev != nullptr)
{
return ev->fflags;
}
return 0;
}
int KYTY_SYSV_ABI KernelGetEventFilter(const KernelEvent* ev)
{
PRINT_NAME();
if (ev != nullptr)
{
return ev->filter;
}
return 0;
}
uintptr_t KYTY_SYSV_ABI KernelGetEventId(const KernelEvent* ev)
{
PRINT_NAME();
if (ev != nullptr)
{
return ev->ident;
}
return 0;
}
void* KYTY_SYSV_ABI KernelGetEventUserData(const KernelEvent* ev)
{
PRINT_NAME();
if (ev != nullptr)
{
return ev->udata;
}
return nullptr;
}
int KYTY_SYSV_ABI KernelGetEventError(const KernelEvent* /*ev*/)
{
PRINT_NAME();
KYTY_NOT_IMPLEMENTED;
return 0;
}
} // namespace Kyty::Libs::LibKernel::EventQueue
#endif // KYTY_EMU_ENABLED
+928
View File
@@ -0,0 +1,928 @@
#include "Emulator/Kernel/FileSystem.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DateTime.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/File.h"
#include "Kyty/Core/Threads.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Libs/Errno.h"
#include "Emulator/Libs/Libs.h"
#include <atomic>
#include <climits>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::LibKernel::FileSystem {
LIB_NAME("libkernel", "libkernel");
constexpr int DESCRIPTOR_MIN = 3;
class MountPoints
{
public:
struct MountPair
{
String dir;
String point;
};
MountPoints() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
virtual ~MountPoints() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(MountPoints);
void Mount(const String& folder, const String& point);
void Umount(const String& folder_or_point);
[[nodiscard]] String GetRealFilename(const String& mounted_file_name);
[[nodiscard]] String GetRealDirectory(const String& mounted_directory);
private:
Vector<MountPair> m_mount_pairs;
Core::Mutex m_mutex;
};
struct File
{
Core::File f;
String name;
String real_name;
std::atomic_bool opened;
std::atomic_bool directory;
Core::Mutex mutex;
Vector<Core::File::DirEntry> dents;
uint32_t dents_index;
};
class FileDescriptors
{
public:
FileDescriptors() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
virtual ~FileDescriptors() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(FileDescriptors);
int CreateDescriptor();
void DeleteDescriptor(int d);
File* GetFile(int d);
File* GetFile(const String& real_name);
void CloseAll();
private:
Vector<File*> m_files;
Core::Mutex m_mutex;
};
static MountPoints* g_mount_points = nullptr;
static FileDescriptors* g_files = nullptr;
static void sec_to_timespec(KernelTimespec* ts, double sec)
{
ts->tv_sec = static_cast<int64_t>(sec);
ts->tv_nsec = static_cast<int64_t>((sec - static_cast<double>(ts->tv_sec)) * 1000000000.0);
}
int FileDescriptors::CreateDescriptor()
{
Core::LockGuard lock(m_mutex);
auto* file = new File {};
file->opened = false;
file->directory = false;
int files_num = static_cast<int>(m_files.Size());
for (int index = 0; index < files_num; index++)
{
if (m_files.At(index) == nullptr)
{
m_files[index] = file;
return index + DESCRIPTOR_MIN;
}
}
m_files.Add(file);
return static_cast<int>(m_files.Size()) + DESCRIPTOR_MIN - 1;
}
void FileDescriptors::DeleteDescriptor(int d)
{
Core::LockGuard lock(m_mutex);
auto index = static_cast<uint32_t>(d - DESCRIPTOR_MIN);
EXIT_IF(!m_files.IndexValid(index));
EXIT_IF(m_files.At(index) == nullptr);
EXIT_IF(m_files.At(index)->opened);
delete m_files.At(index);
m_files[index] = nullptr;
}
File* FileDescriptors::GetFile(int d)
{
Core::LockGuard lock(m_mutex);
auto index = static_cast<uint32_t>(d - DESCRIPTOR_MIN);
EXIT_IF(!m_files.IndexValid(index));
return m_files.At(index);
}
File* FileDescriptors::GetFile(const String& real_name)
{
Core::LockGuard lock(m_mutex);
for (auto* f: m_files)
{
if (f != nullptr && f->real_name == real_name)
{
return f;
}
}
return nullptr;
}
void FileDescriptors::CloseAll()
{
Core::LockGuard lock(m_mutex);
for (auto& f: m_files)
{
if (f != nullptr && f->opened)
{
f->f.Close();
delete f;
f = nullptr;
}
}
}
void MountPoints::Mount(const String& folder, const String& point)
{
Core::LockGuard lock(m_mutex);
auto folder_str = folder.FixDirectorySlash();
auto point_str = point.FixDirectorySlash();
Umount(folder_str);
Umount(point_str);
MountPair p;
p.dir = folder_str;
p.point = point_str;
m_mount_pairs.Add(p);
}
void MountPoints::Umount(const String& folder_or_point)
{
Core::LockGuard lock(m_mutex);
auto folder_or_point_str = folder_or_point.FixDirectorySlash();
if (auto index =
m_mount_pairs.Find(folder_or_point_str, [](const MountPair& p, const String& s) { return p.dir == s || p.point == s; });
m_mount_pairs.IndexValid(index))
{
m_mount_pairs.RemoveAt(index);
}
}
String MountPoints::GetRealFilename(const String& mounted_file_name)
{
Core::LockGuard lock(m_mutex);
auto mounted_path = mounted_file_name.FixFilenameSlash().DirectoryWithoutFilename();
if (auto index = m_mount_pairs.Find(mounted_path, [](const MountPair& p, const String& s) { return s.StartsWith(p.point); });
m_mount_pairs.IndexValid(index))
{
const auto& p = m_mount_pairs.At(index);
return p.dir + mounted_file_name.RemoveFirst(p.point.Size());
}
return mounted_file_name;
}
String MountPoints::GetRealDirectory(const String& mounted_directory)
{
Core::LockGuard lock(m_mutex);
auto mounted_path = mounted_directory.FixDirectorySlash();
if (auto index = m_mount_pairs.Find(mounted_path, [](const MountPair& p, const String& s) { return s.StartsWith(p.point); });
m_mount_pairs.IndexValid(index))
{
const auto& p = m_mount_pairs.At(index);
return p.dir + mounted_directory.RemoveFirst(p.point.Size());
}
return mounted_directory;
}
KYTY_SUBSYSTEM_INIT(FileSystem)
{
g_mount_points = new MountPoints;
g_files = new FileDescriptors;
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(FileSystem)
{
if (g_files != nullptr)
{
g_files->CloseAll();
}
}
KYTY_SUBSYSTEM_DESTROY(FileSystem)
{
if (g_files != nullptr)
{
g_files->CloseAll();
}
}
void Mount(const String& folder, const String& point)
{
EXIT_IF(g_mount_points == nullptr);
g_mount_points->Mount(folder, point);
}
void Umount(const String& folder_or_point)
{
EXIT_IF(g_mount_points == nullptr);
g_mount_points->Umount(folder_or_point);
}
String GetRealFilename(const String& mounted_file_name)
{
EXIT_IF(g_mount_points == nullptr);
return g_mount_points->GetRealFilename(mounted_file_name);
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode)
{
PRINT_NAME();
EXIT_IF(g_mount_points == nullptr || g_files == nullptr);
if (path == nullptr)
{
return KERNEL_ERROR_EINVAL;
}
auto flags_u = static_cast<uint32_t>(flags);
printf("\tpath = %s\n", path);
printf("\tflags = %08" PRIx32 "\n", flags_u);
printf("\tmode = %04" PRIx16 "\n", mode);
bool nonblock = (flags_u & 0x0004u) != 0;
bool append = (flags_u & 0x0008u) != 0;
bool fsync = (flags_u & 0x0080u) != 0;
bool sync = (flags_u & 0x0080u) != 0;
bool creat = (flags_u & 0x0200u) != 0;
bool trunc = (flags_u & 0x0400u) != 0;
bool excl = (flags_u & 0x0800u) != 0;
bool dsync = (flags_u & 0x1000u) != 0;
bool direct = (flags_u & 0x00010000u) != 0;
bool directory = (flags_u & 0x00020000u) != 0;
EXIT_NOT_IMPLEMENTED(append || fsync || sync || excl || dsync || direct);
EXIT_NOT_IMPLEMENTED(nonblock && !directory);
flags_u &= 0x3u;
Core::File::Mode rw_mode = Core::File::Mode::Read;
switch (flags_u)
{
case 0: rw_mode = Core::File::Mode::Read; break;
case 1: rw_mode = Core::File::Mode::Write; break;
case 2: rw_mode = Core::File::Mode::ReadWrite; break;
default: EXIT("invalid flag_u: %u\n", flags_u);
}
EXIT_NOT_IMPLEMENTED(directory && rw_mode != Core::File::Mode::Read);
EXIT_NOT_IMPLEMENTED(directory && (trunc || creat));
int descriptor = g_files->CreateDescriptor();
auto* file = g_files->GetFile(descriptor);
EXIT_IF(file == nullptr || file->opened || file->directory);
file->name = path;
file->real_name = (directory ? g_mount_points->GetRealDirectory(file->name) : g_mount_points->GetRealFilename(file->name));
if (trunc && rw_mode == Core::File::Mode::Read)
{
return KERNEL_ERROR_EACCES;
}
if (directory)
{
if (!Core::File::IsDirectoryExisting(file->real_name))
{
g_files->DeleteDescriptor(descriptor);
return KERNEL_ERROR_ENOTDIR;
}
file->dents = Core::File::GetDirEntries(file->real_name);
file->dents_index = 0;
file->directory = true;
printf("\tOpen dir: " FG_WHITE BOLD "%s" DEFAULT ", entries = %" PRIu32 ", " FG_GREEN "[ok]" FG_DEFAULT "\n",
file->real_name.C_Str(), file->dents.Size());
for (const auto& f: file->dents)
{
printf("\t\t%s %s\n", f.is_file ? "[file]" : "[dir ]", f.name.C_Str());
}
} else
{
bool result = false;
EXIT_NOT_IMPLEMENTED(Core::File::IsDirectoryExisting(file->real_name));
if (creat)
{
result = file->f.Create(file->real_name);
printf("\tCreate: " FG_WHITE BOLD "%s" DEFAULT ", %s\n", file->real_name.C_Str(),
(result ? FG_GREEN "[ok]" FG_DEFAULT : FG_RED "[fail]" FG_DEFAULT));
} else
{
result = file->f.Open(file->real_name, rw_mode);
printf("\tOpen: " FG_WHITE BOLD "%s" DEFAULT ", %s\n", file->real_name.C_Str(),
(result ? FG_GREEN "[ok]" FG_DEFAULT : FG_RED "[fail]" FG_DEFAULT));
}
EXIT_NOT_IMPLEMENTED(creat && !trunc);
if (result && trunc)
{
result = file->f.Truncate(0);
}
if (!result || file->f.IsInvalid())
{
g_files->DeleteDescriptor(descriptor);
return KERNEL_ERROR_EACCES;
}
}
file->opened = true;
return descriptor;
}
int KYTY_SYSV_ABI KernelClose(int d)
{
PRINT_NAME();
EXIT_IF(g_files == nullptr);
if (d < DESCRIPTOR_MIN)
{
return KERNEL_ERROR_EPERM;
}
auto* file = g_files->GetFile(d);
if (file == nullptr)
{
return KERNEL_ERROR_EBADF;
}
EXIT_IF(!file->opened);
if (!file->directory)
{
file->f.Close();
}
file->opened = false;
printf("\tClose: " FG_WHITE BOLD "%s" DEFAULT "\n", file->real_name.C_Str());
g_files->DeleteDescriptor(d);
return OK;
}
int64_t KYTY_SYSV_ABI KernelRead(int d, void* buf, size_t nbytes)
{
PRINT_NAME();
EXIT_IF(g_files == nullptr);
if (d < DESCRIPTOR_MIN)
{
return KERNEL_ERROR_EPERM;
}
if (buf == nullptr)
{
return KERNEL_ERROR_EFAULT;
}
auto* file = g_files->GetFile(d);
if (file == nullptr)
{
return KERNEL_ERROR_EBADF;
}
EXIT_NOT_IMPLEMENTED(file->directory);
EXIT_IF(!file->opened);
EXIT_NOT_IMPLEMENTED(nbytes > UINT_MAX);
file->mutex.Lock();
bool is_invalid = file->f.IsInvalid();
uint32_t bytes_read = 0;
file->f.Read(buf, static_cast<uint32_t>(nbytes), &bytes_read);
file->mutex.Unlock();
if (is_invalid)
{
printf("\tfile is invalid\n");
return KERNEL_ERROR_EIO;
}
printf("\tRead %u bytes from: " FG_WHITE BOLD "%s" DEFAULT "\n", bytes_read, file->real_name.C_Str());
return bytes_read;
}
int64_t KYTY_SYSV_ABI KernelWrite(int d, const void* buf, size_t nbytes)
{
PRINT_NAME();
EXIT_IF(g_files == nullptr);
if (d < DESCRIPTOR_MIN)
{
return KERNEL_ERROR_EPERM;
}
if (buf == nullptr)
{
return KERNEL_ERROR_EFAULT;
}
auto* file = g_files->GetFile(d);
if (file == nullptr)
{
return KERNEL_ERROR_EBADF;
}
EXIT_NOT_IMPLEMENTED(file->directory);
EXIT_IF(!file->opened);
EXIT_NOT_IMPLEMENTED(nbytes > UINT_MAX);
file->mutex.Lock();
bool is_invalid = file->f.IsInvalid();
uint32_t bytes_written = 0;
file->f.Write(buf, static_cast<uint32_t>(nbytes), &bytes_written);
file->mutex.Unlock();
if (is_invalid)
{
printf("\tfile is invalid\n");
return KERNEL_ERROR_EIO;
}
printf("\tWrite %u bytes to: " FG_WHITE BOLD "%s" DEFAULT "\n", bytes_written, file->real_name.C_Str());
return bytes_written;
}
int64_t KYTY_SYSV_ABI KernelPread(int d, void* buf, size_t nbytes, int64_t offset)
{
PRINT_NAME();
EXIT_IF(g_files == nullptr);
if (d < DESCRIPTOR_MIN)
{
return KERNEL_ERROR_EPERM;
}
if (buf == nullptr)
{
return KERNEL_ERROR_EFAULT;
}
if (offset < 0)
{
return KERNEL_ERROR_EINVAL;
}
auto* file = g_files->GetFile(d);
if (file == nullptr)
{
return KERNEL_ERROR_EBADF;
}
EXIT_NOT_IMPLEMENTED(file->directory);
EXIT_IF(!file->opened);
EXIT_NOT_IMPLEMENTED(nbytes > UINT_MAX);
file->mutex.Lock();
bool is_invalid = file->f.IsInvalid();
auto pos = file->f.Tell();
uint32_t bytes_read = 0;
file->f.Seek(offset);
file->f.Read(buf, static_cast<uint32_t>(nbytes), &bytes_read);
file->f.Seek(pos);
file->mutex.Unlock();
if (is_invalid)
{
printf("\tfile is invalid\n");
return KERNEL_ERROR_EIO;
}
printf("\tRead %u bytes (pos = %" PRId64 ") from: " FG_WHITE BOLD "%s" DEFAULT "\n", bytes_read, offset, file->real_name.C_Str());
return bytes_read;
}
int64_t KYTY_SYSV_ABI KernelPwrite(int d, const void* buf, size_t nbytes, int64_t offset)
{
PRINT_NAME();
EXIT_IF(g_files == nullptr);
if (d < DESCRIPTOR_MIN)
{
return KERNEL_ERROR_EPERM;
}
if (buf == nullptr)
{
return KERNEL_ERROR_EFAULT;
}
if (offset < 0)
{
return KERNEL_ERROR_EINVAL;
}
auto* file = g_files->GetFile(d);
if (file == nullptr)
{
return KERNEL_ERROR_EBADF;
}
EXIT_NOT_IMPLEMENTED(file->directory);
EXIT_IF(!file->opened);
EXIT_NOT_IMPLEMENTED(nbytes > UINT_MAX);
file->mutex.Lock();
bool is_invalid = file->f.IsInvalid();
auto pos = file->f.Tell();
uint32_t bytes_written = 0;
file->f.Seek(offset);
file->f.Write(buf, static_cast<uint32_t>(nbytes), &bytes_written);
file->f.Seek(pos);
file->mutex.Unlock();
if (is_invalid)
{
printf("\tfile is invalid\n");
return KERNEL_ERROR_EIO;
}
printf("\tWrite %u bytes (pos = %" PRId64 ") to: " FG_WHITE BOLD "%s" DEFAULT "\n", bytes_written, offset, file->real_name.C_Str());
return bytes_written;
}
int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence)
{
PRINT_NAME();
EXIT_IF(g_files == nullptr);
if (d < DESCRIPTOR_MIN)
{
return KERNEL_ERROR_EPERM;
}
auto* file = g_files->GetFile(d);
if (file == nullptr)
{
return KERNEL_ERROR_EBADF;
}
EXIT_NOT_IMPLEMENTED(file->directory);
EXIT_IF(!file->opened);
file->mutex.Lock();
bool is_invalid = file->f.IsInvalid();
if (whence == 1)
{
offset = static_cast<int64_t>(file->f.Tell()) + offset;
whence = 0;
}
if (whence == 2)
{
offset = static_cast<int64_t>(file->f.Size()) + offset;
whence = 0;
}
EXIT_NOT_IMPLEMENTED(whence != 0);
if (offset < 0)
{
return KERNEL_ERROR_EINVAL;
}
file->f.Seek(offset);
auto pos = static_cast<int64_t>(file->f.Tell());
EXIT_IF(pos != offset);
file->mutex.Unlock();
if (is_invalid)
{
printf("\tfile is invalid\n");
return KERNEL_ERROR_EIO;
}
printf("\tLseek (pos = %" PRId64 ") to: " FG_WHITE BOLD "%s" DEFAULT "\n", offset, file->real_name.C_Str());
return pos;
}
int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb)
{
PRINT_NAME();
EXIT_IF(g_mount_points == nullptr);
if (path == nullptr || sb == nullptr)
{
return KERNEL_ERROR_EINVAL;
}
printf("\tKernelStat: %s\n", path);
String path_s = String::FromUtf8(path);
auto real_file_name = g_mount_points->GetRealFilename(path_s);
auto real_directory = g_mount_points->GetRealDirectory(path_s);
bool is_dir = Core::File::IsDirectoryExisting(real_file_name) || Core::File::IsDirectoryExisting(real_directory);
bool is_file = Core::File::IsFileExisting(real_file_name);
if (!is_dir && !is_file)
{
printf("\tfile not found\n");
return KERNEL_ERROR_ENOENT;
}
EXIT_NOT_IMPLEMENTED(is_dir && is_file);
memset(sb, 0, sizeof(FileStat));
sb->st_mode = 0000777u | (is_dir ? 0040000u : 0100000u);
Core::DateTime at;
Core::DateTime wt;
if (is_dir)
{
sb->st_size = 0;
sb->st_blksize = 512;
sb->st_blocks = 0;
} else
{
sb->st_size = static_cast<int64_t>(Core::File::Size(real_file_name));
sb->st_blksize = 512;
sb->st_blocks = (sb->st_size + 511) / 512;
Core::File::GetLastAccessAndWriteTimeUTC(real_file_name, &at, &wt);
}
sec_to_timespec(&sb->st_atim, at.ToUnix());
sec_to_timespec(&sb->st_mtim, wt.ToUnix());
sb->st_ctim = sb->st_atim;
sb->st_birthtim = sb->st_mtim;
return OK;
}
int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb)
{
PRINT_NAME();
EXIT_IF(g_files == nullptr);
if (d < DESCRIPTOR_MIN)
{
return KERNEL_ERROR_EPERM;
}
if (sb == nullptr)
{
return KERNEL_ERROR_EFAULT;
}
auto* file = g_files->GetFile(d);
if (file == nullptr)
{
return KERNEL_ERROR_EBADF;
}
EXIT_IF(!file->opened);
printf("\tKernelFstat: %s\n", file->real_name.C_Str());
memset(sb, 0, sizeof(FileStat));
sb->st_mode = 0000777u | (file->directory ? 0040000u : 0100000u);
Core::DateTime at;
Core::DateTime wt;
if (!file->directory)
{
file->mutex.Lock();
bool is_invalid = file->f.IsInvalid();
auto size = file->f.Size();
file->f.GetLastAccessAndWriteTimeUTC(&at, &wt);
file->mutex.Unlock();
if (is_invalid)
{
printf("\tfile is invalid\n");
return KERNEL_ERROR_EIO;
}
sb->st_size = static_cast<int64_t>(size);
sb->st_blksize = 512;
sb->st_blocks = (sb->st_size + 511) / 512;
} else
{
sb->st_size = 0;
sb->st_blksize = 512;
sb->st_blocks = 0;
}
sec_to_timespec(&sb->st_atim, at.ToUnix());
sec_to_timespec(&sb->st_mtim, wt.ToUnix());
sb->st_ctim = sb->st_atim;
sb->st_birthtim = sb->st_mtim;
return OK;
}
int KYTY_SYSV_ABI KernelUnlink(const char* path)
{
PRINT_NAME();
EXIT_IF(g_mount_points == nullptr);
EXIT_IF(g_files == nullptr);
if (path == nullptr)
{
return KERNEL_ERROR_EINVAL;
}
auto path_s = String::FromUtf8(path);
auto real_file_name = g_mount_points->GetRealFilename(path_s);
auto real_directory = g_mount_points->GetRealDirectory(path_s);
EXIT_NOT_IMPLEMENTED(g_files->GetFile(real_file_name) != nullptr);
EXIT_NOT_IMPLEMENTED(g_files->GetFile(real_directory) != nullptr);
bool is_dir = Core::File::IsDirectoryExisting(real_file_name) || Core::File::IsDirectoryExisting(real_directory);
bool is_file = Core::File::IsFileExisting(real_file_name);
if (is_dir)
{
return KERNEL_ERROR_EPERM;
}
if (!is_file)
{
return KERNEL_ERROR_ENOENT;
}
bool ok = Core::File::DeleteFile(real_file_name);
if (!ok)
{
return KERNEL_ERROR_EIO;
}
printf("\tKernelUnlink: %s\n", path);
return OK;
}
int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* basep)
{
PRINT_NAME();
EXIT_IF(g_files == nullptr);
if (fd < DESCRIPTOR_MIN)
{
return KERNEL_ERROR_EBADF;
}
if (buf == nullptr)
{
return KERNEL_ERROR_EFAULT;
}
auto* file = g_files->GetFile(fd);
if (file == nullptr)
{
return KERNEL_ERROR_EBADF;
}
if (!file->directory || nbytes < 512 || file->dents_index > file->dents.Size())
{
return KERNEL_ERROR_EINVAL;
}
EXIT_IF(!file->opened);
printf("\tdir = %s\n", file->real_name.C_Str());
printf("\tnbytes = %d\n", nbytes);
printf("\tindex = %d\n", file->dents_index);
if (basep != nullptr)
{
*basep = file->dents_index;
}
if (file->dents_index == file->dents.Size())
{
return 0;
}
const auto& entry = file->dents.At(file->dents_index++);
auto str = entry.name.utf8_str();
auto str_size = str.Size() - 1;
EXIT_NOT_IMPLEMENTED(str_size > 255);
printf("\tname = %s\n", str.GetDataConst());
*reinterpret_cast<uint32_t*>(buf + 0) = entry.name.Hash();
*reinterpret_cast<uint16_t*>(buf + 4) = 512;
*reinterpret_cast<uint8_t*>(buf + 6) = (entry.is_file ? 8 : 4);
*reinterpret_cast<uint8_t*>(buf + 7) = static_cast<uint8_t>(str_size);
strncpy(buf + 8, str.GetDataConst(), 255);
buf[8 + 255] = '\0';
return 512;
}
} // namespace Kyty::Libs::LibKernel::FileSystem
#endif // KYTY_EMU_ENABLED
+604
View File
@@ -0,0 +1,604 @@
#include "Emulator/Kernel/Memory.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/MagicEnum.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Threads.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Graphics/GpuMemory.h"
#include "Emulator/Graphics/GraphicsRun.h"
#include "Emulator/Graphics/Window.h"
#include "Emulator/Libs/Errno.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/VirtualMemory.h"
#include <algorithm>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::LibKernel::Memory {
namespace VirtualMemory = Loader::VirtualMemory;
LIB_NAME("libkernel", "libkernel");
class PhysicalMemory
{
public:
struct AllocatedBlock
{
uint64_t start_addr;
uint64_t size;
uint64_t map_vaddr;
uint64_t map_size;
int prot;
VirtualMemory::Mode mode;
Graphics::GpuMemoryMode gpu_mode;
};
PhysicalMemory() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
virtual ~PhysicalMemory() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(PhysicalMemory);
static uint64_t Size() { return static_cast<uint64_t>(5376) * 1024 * 1024; }
bool Alloc(uint64_t search_start, uint64_t search_end, size_t len, size_t alignment, uint64_t* phys_addr_out);
bool Release(uint64_t start, size_t len, uint64_t* vaddr, uint64_t* size, Graphics::GpuMemoryMode* gpu_mode);
bool Map(uint64_t vaddr, uint64_t phys_addr, size_t len, int prot, VirtualMemory::Mode mode, Graphics::GpuMemoryMode gpu_mode);
bool Unmap(uint64_t vaddr, uint64_t size, Graphics::GpuMemoryMode* gpu_mode);
bool Find(uint64_t vaddr, uint64_t* base_addr, size_t* len, int* prot, VirtualMemory::Mode* mode, Graphics::GpuMemoryMode* gpu_mode);
private:
Vector<AllocatedBlock> m_allocated;
Core::Mutex m_mutex;
};
class FlexibleMemory
{
public:
struct AllocatedBlock
{
uint64_t map_vaddr;
uint64_t map_size;
int prot;
VirtualMemory::Mode mode;
Graphics::GpuMemoryMode gpu_mode;
};
FlexibleMemory() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
virtual ~FlexibleMemory() { KYTY_NOT_IMPLEMENTED; }
KYTY_CLASS_NO_COPY(FlexibleMemory);
bool Map(uint64_t vaddr, size_t len, int prot, VirtualMemory::Mode mode, Graphics::GpuMemoryMode gpu_mode);
bool Unmap(uint64_t vaddr, uint64_t size, Graphics::GpuMemoryMode* gpu_mode);
bool Find(uint64_t vaddr, uint64_t* base_addr, size_t* len, int* prot, VirtualMemory::Mode* mode, Graphics::GpuMemoryMode* gpu_mode);
private:
Vector<AllocatedBlock> m_allocated;
Core::Mutex m_mutex;
};
static PhysicalMemory* g_physical_memory = nullptr;
static FlexibleMemory* g_flexible_memory = nullptr;
KYTY_SUBSYSTEM_INIT(Memory)
{
g_physical_memory = new PhysicalMemory;
g_flexible_memory = new FlexibleMemory;
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Memory) {}
KYTY_SUBSYSTEM_DESTROY(Memory) {}
static uint64_t get_aligned_pos(uint64_t pos, size_t align)
{
return (align != 0 ? (pos + (align - 1)) & ~(align - 1) : pos);
}
bool PhysicalMemory::Alloc(uint64_t search_start, uint64_t search_end, size_t len, size_t alignment, uint64_t* phys_addr_out)
{
if (phys_addr_out == nullptr)
{
return false;
}
Core::LockGuard lock(m_mutex);
uint64_t free_pos = 0;
for (const auto& b: m_allocated)
{
uint64_t n = b.start_addr + b.size;
if (n > free_pos)
{
free_pos = n;
}
}
free_pos = get_aligned_pos(free_pos, alignment);
if (free_pos >= search_start && free_pos + len <= search_end)
{
AllocatedBlock b {};
b.size = len;
b.start_addr = free_pos;
b.gpu_mode = Graphics::GpuMemoryMode::NoAccess;
b.map_size = 0;
b.map_vaddr = 0;
b.prot = 0;
b.mode = VirtualMemory::Mode::NoAccess;
m_allocated.Add(b);
*phys_addr_out = free_pos;
return true;
}
return false;
}
bool PhysicalMemory::Release(uint64_t start, size_t len, uint64_t* vaddr, uint64_t* size, Graphics::GpuMemoryMode* gpu_mode)
{
EXIT_IF(vaddr == nullptr);
EXIT_IF(size == nullptr);
EXIT_IF(gpu_mode == nullptr);
Core::LockGuard lock(m_mutex);
uint32_t index = 0;
for (auto& b: m_allocated)
{
if (start == b.start_addr && len == b.size)
{
*vaddr = b.map_vaddr;
*size = b.map_size;
*gpu_mode = b.gpu_mode;
m_allocated.RemoveAt(index);
return true;
}
index++;
}
return false;
}
bool PhysicalMemory::Map(uint64_t vaddr, uint64_t phys_addr, size_t len, int prot, VirtualMemory::Mode mode,
Graphics::GpuMemoryMode gpu_mode)
{
Core::LockGuard lock(m_mutex);
for (auto& b: m_allocated)
{
if (phys_addr >= b.start_addr && phys_addr < b.start_addr + b.size)
{
if (b.map_vaddr != 0 || b.map_size != 0)
{
return false;
}
b.map_vaddr = vaddr;
b.map_size = len;
b.prot = prot;
b.mode = mode;
b.gpu_mode = gpu_mode;
return true;
}
}
return false;
}
bool PhysicalMemory::Unmap(uint64_t vaddr, uint64_t size, Graphics::GpuMemoryMode* gpu_mode)
{
EXIT_IF(gpu_mode == nullptr);
Core::LockGuard lock(m_mutex);
for (auto& b: m_allocated)
{
if (b.map_vaddr == vaddr && b.map_size == size)
{
*gpu_mode = b.gpu_mode;
b.gpu_mode = Graphics::GpuMemoryMode::NoAccess;
b.map_size = 0;
b.map_vaddr = 0;
b.prot = 0;
b.mode = VirtualMemory::Mode::NoAccess;
return true;
}
}
return false;
}
bool PhysicalMemory::Find(uint64_t vaddr, uint64_t* base_addr, size_t* len, int* prot, VirtualMemory::Mode* mode,
Graphics::GpuMemoryMode* gpu_mode)
{
Core::LockGuard lock(m_mutex);
return std::any_of(m_allocated.begin(), m_allocated.end(),
[vaddr, base_addr, len, prot, mode, gpu_mode](auto& b)
{
if (vaddr >= b.map_vaddr && vaddr < b.map_vaddr + b.map_size)
{
if (base_addr != nullptr)
{
*base_addr = b.map_vaddr;
}
if (len != nullptr)
{
*len = b.map_size;
}
if (prot != nullptr)
{
*prot = b.prot;
}
if (mode != nullptr)
{
*mode = b.mode;
}
if (gpu_mode != nullptr)
{
*gpu_mode = b.gpu_mode;
}
return true;
}
return false;
});
}
bool FlexibleMemory::Map(uint64_t vaddr, size_t len, int prot, VirtualMemory::Mode mode, Graphics::GpuMemoryMode gpu_mode)
{
Core::LockGuard lock(m_mutex);
AllocatedBlock b {};
b.map_vaddr = vaddr;
b.map_size = len;
b.prot = prot;
b.mode = mode;
b.gpu_mode = gpu_mode;
m_allocated.Add(b);
return true;
}
bool FlexibleMemory::Unmap(uint64_t vaddr, uint64_t size, Graphics::GpuMemoryMode* gpu_mode)
{
EXIT_IF(gpu_mode == nullptr);
Core::LockGuard lock(m_mutex);
uint32_t index = 0;
for (auto& b: m_allocated)
{
if (b.map_vaddr == vaddr && b.map_size == size)
{
*gpu_mode = b.gpu_mode;
m_allocated.RemoveAt(index);
return true;
}
index++;
}
return false;
}
bool FlexibleMemory::Find(uint64_t vaddr, uint64_t* base_addr, size_t* len, int* prot, VirtualMemory::Mode* mode,
Graphics::GpuMemoryMode* gpu_mode)
{
Core::LockGuard lock(m_mutex);
return std::any_of(m_allocated.begin(), m_allocated.end(),
[vaddr, base_addr, len, prot, mode, gpu_mode](auto& b)
{
if (vaddr >= b.map_vaddr && vaddr < b.map_vaddr + b.map_size)
{
if (base_addr != nullptr)
{
*base_addr = b.map_vaddr;
}
if (len != nullptr)
{
*len = b.map_size;
}
if (prot != nullptr)
{
*prot = b.prot;
}
if (mode != nullptr)
{
*mode = b.mode;
}
if (gpu_mode != nullptr)
{
*gpu_mode = b.gpu_mode;
}
return true;
}
return false;
});
}
int32_t KYTY_SYSV_ABI KernelMapNamedFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags, const char* name)
{
PRINT_NAME();
EXIT_IF(g_flexible_memory == nullptr);
EXIT_NOT_IMPLEMENTED(addr_in_out == nullptr);
EXIT_NOT_IMPLEMENTED(flags != 0);
VirtualMemory::Mode mode = VirtualMemory::Mode::NoAccess;
Graphics::GpuMemoryMode gpu_mode = Graphics::GpuMemoryMode::NoAccess;
switch (prot)
{
case 0: mode = VirtualMemory::Mode::NoAccess; break;
case 1: mode = VirtualMemory::Mode::Read; break;
case 2:
case 3: mode = VirtualMemory::Mode::ReadWrite; break;
case 4: mode = VirtualMemory::Mode::Execute; break;
case 5: mode = VirtualMemory::Mode::ExecuteRead; break;
case 6:
case 7: mode = VirtualMemory::Mode::ExecuteReadWrite; break;
default: EXIT("unknown prot: %d\n", prot);
}
auto in_addr = reinterpret_cast<uint64_t>(*addr_in_out);
auto out_addr = VirtualMemory::Alloc(in_addr, len, mode);
*addr_in_out = reinterpret_cast<void*>(out_addr);
if (!g_flexible_memory->Map(out_addr, len, prot, mode, gpu_mode))
{
printf(FG_RED "\t[Fail]\n" FG_DEFAULT);
VirtualMemory::Free(out_addr);
return KERNEL_ERROR_ENOMEM;
}
printf("\tin_addr = 0x%016" PRIx64 "\n", in_addr);
printf("\tout_addr = 0x%016" PRIx64 "\n", out_addr);
printf("\tsize = %" PRIu64 "\n", len);
printf("\tmode = %s\n", Core::EnumName(mode).C_Str());
printf("\tname = %s\n", name);
if (out_addr == 0)
{
return KERNEL_ERROR_ENOMEM;
}
return OK;
}
int KYTY_SYSV_ABI KernelMunmap(uint64_t vaddr, size_t len)
{
PRINT_NAME();
printf("\t start = 0x%016" PRIx64 "\n", vaddr);
printf("\t len = 0x%016" PRIx64 "\n", len);
EXIT_IF(g_physical_memory == nullptr);
EXIT_IF(g_flexible_memory == nullptr);
if (vaddr < 0 || len == 0)
{
return KERNEL_ERROR_EINVAL;
}
Graphics::GpuMemoryMode gpu_mode = Graphics::GpuMemoryMode::NoAccess;
bool result = g_physical_memory->Unmap(vaddr, len, &gpu_mode);
if (!result)
{
result = g_flexible_memory->Unmap(vaddr, len, &gpu_mode);
}
EXIT_NOT_IMPLEMENTED(!result);
if (vaddr != 0 || len != 0)
{
VirtualMemory::Free(vaddr);
}
if (gpu_mode != Graphics::GpuMemoryMode::NoAccess)
{
Graphics::GraphicsRunWait();
Graphics::GpuMemoryFree(Graphics::WindowGetGraphicContext(), vaddr, len);
}
return OK;
}
size_t KYTY_SYSV_ABI KernelGetDirectMemorySize()
{
PRINT_NAME();
return PhysicalMemory::Size();
}
int KYTY_SYSV_ABI KernelAllocateDirectMemory(int64_t search_start, int64_t search_end, size_t len, size_t alignment, int memory_type,
int64_t* phys_addr_out)
{
PRINT_NAME();
EXIT_IF(g_physical_memory == nullptr);
printf("\t search_start = 0x%016" PRIx64 "\n", search_start);
printf("\t search_end = 0x%016" PRIx64 "\n", search_end);
printf("\t len = 0x%016" PRIx64 "\n", len);
printf("\t alignment = 0x%016" PRIx64 "\n", alignment);
printf("\t memory_type = %d\n", memory_type);
if (search_start < 0 || search_end <= search_start || len == 0 || phys_addr_out == nullptr)
{
return KERNEL_ERROR_EINVAL;
}
uint64_t addr = 0;
if (!g_physical_memory->Alloc(search_start, search_end, len, alignment, &addr))
{
printf(FG_RED "\t[Fail]\n" FG_DEFAULT);
return KERNEL_ERROR_EAGAIN;
}
*phys_addr_out = static_cast<int64_t>(addr);
printf("\tphys_addr = %016" PRIx64 "\n", addr);
printf(FG_GREEN "\t[Ok]\n" FG_DEFAULT);
return OK;
}
int KYTY_SYSV_ABI KernelReleaseDirectMemory(int64_t start, size_t len)
{
PRINT_NAME();
printf("\t start = 0x%016" PRIx64 "\n", start);
printf("\t len = 0x%016" PRIx64 "\n", len);
EXIT_IF(g_physical_memory == nullptr);
if (start < 0 || len == 0)
{
return KERNEL_ERROR_EINVAL;
}
uint64_t vaddr = 0;
uint64_t size = 0;
Graphics::GpuMemoryMode gpu_mode = Graphics::GpuMemoryMode::NoAccess;
bool result = g_physical_memory->Release(start, len, &vaddr, &size, &gpu_mode);
EXIT_NOT_IMPLEMENTED(!result);
if (vaddr != 0 || size != 0)
{
VirtualMemory::Free(vaddr);
}
if (gpu_mode != Graphics::GpuMemoryMode::NoAccess)
{
Graphics::GraphicsRunWait();
Graphics::GpuMemoryFree(Graphics::WindowGetGraphicContext(), vaddr, size);
}
return OK;
}
int KYTY_SYSV_ABI KernelMapDirectMemory(void** addr, size_t len, int prot, int flags, int64_t direct_memory_start, size_t alignment)
{
PRINT_NAME();
EXIT_IF(g_physical_memory == nullptr);
// EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
EXIT_NOT_IMPLEMENTED(addr == nullptr);
EXIT_NOT_IMPLEMENTED(flags != 0);
VirtualMemory::Mode mode = VirtualMemory::Mode::NoAccess;
Graphics::GpuMemoryMode gpu_mode = Graphics::GpuMemoryMode::NoAccess;
switch (prot)
{
case 0x00: mode = VirtualMemory::Mode::NoAccess; break;
case 0x01: mode = VirtualMemory::Mode::Read; break;
case 0x02:
case 0x03: mode = VirtualMemory::Mode::ReadWrite; break;
case 0x04: mode = VirtualMemory::Mode::Execute; break;
case 0x05: mode = VirtualMemory::Mode::ExecuteRead; break;
case 0x06:
case 0x07: mode = VirtualMemory::Mode::ExecuteReadWrite; break;
case 0x32:
case 0x33:
mode = VirtualMemory::Mode::ReadWrite;
gpu_mode = Graphics::GpuMemoryMode::ReadWrite;
break;
default: EXIT("unknown prot: %d\n", prot);
}
auto in_addr = reinterpret_cast<uint64_t>(*addr);
auto out_addr = VirtualMemory::AllocAligned(in_addr, len, mode, alignment);
*addr = reinterpret_cast<void*>(out_addr);
printf("\tin_addr = 0x%016" PRIx64 "\n", in_addr);
printf("\tout_addr = 0x%016" PRIx64 "\n", out_addr);
printf("\tsize = 0x%016" PRIx64 "\n", len);
printf("\tmode = %s\n", Core::EnumName(mode).C_Str());
printf("\talign = 0x%016" PRIx64 "\n", alignment);
printf("\tgpu_mode = %s\n", Core::EnumName(gpu_mode).C_Str());
if (out_addr == 0)
{
return KERNEL_ERROR_ENOMEM;
}
if (!g_physical_memory->Map(out_addr, direct_memory_start, len, prot, mode, gpu_mode))
{
printf(FG_RED "\t[Fail]\n" FG_DEFAULT);
VirtualMemory::Free(out_addr);
return KERNEL_ERROR_EBUSY;
}
if (gpu_mode != Graphics::GpuMemoryMode::NoAccess)
{
Graphics::GpuMemorySetAllocatedRange(out_addr, len);
}
printf(FG_GREEN "\t[Ok]\n" FG_DEFAULT);
return OK;
}
int KYTY_SYSV_ABI KernelQueryMemoryProtection(void* addr, void** start, void** end, int* prot)
{
PRINT_NAME();
EXIT_IF(g_physical_memory == nullptr);
EXIT_IF(g_flexible_memory == nullptr);
EXIT_NOT_IMPLEMENTED(addr == nullptr);
size_t len = 0;
int p = 0;
uint64_t base = 0;
if (!g_physical_memory->Find(reinterpret_cast<uint64_t>(addr), &base, &len, &p, nullptr, nullptr))
{
if (!g_flexible_memory->Find(reinterpret_cast<uint64_t>(addr), &base, &len, &p, nullptr, nullptr))
{
return KERNEL_ERROR_EACCES;
}
}
if (start != nullptr)
{
*start = reinterpret_cast<void*>(base);
}
if (end != nullptr)
{
*end = reinterpret_cast<void*>(base + len - 1);
}
if (prot != nullptr)
{
*prot = p;
}
return OK;
}
} // namespace Kyty::Libs::LibKernel::Memory
#endif // KYTY_EMU_ENABLED
File diff suppressed because it is too large Load Diff
+262
View File
@@ -0,0 +1,262 @@
#include "Kyty/Core/Common.h"
#include "Kyty/Core/Core.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/MagicEnum.h"
#include "Kyty/Core/Singleton.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Subsystems.h"
#include "Kyty/Core/Threads.h"
#include "Kyty/Scripts/Scripts.h"
#include "Emulator/Common.h"
#include "Emulator/Config.h"
#include "Emulator/Controller.h"
#include "Emulator/Graphics/Graphics.h"
#include "Emulator/Graphics/Shader.h"
#include "Emulator/Graphics/Window.h"
#include "Emulator/Kernel/FileSystem.h"
#include "Emulator/Kernel/Memory.h"
#include "Emulator/Kernel/Pthread.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/Profiler.h"
#include "Emulator/RuntimeLinker.h"
#include "Emulator/Timer.h"
#include "Emulator/VirtualMemory.h"
#include <cstdlib>
namespace Kyty::Emulator {
#ifdef KYTY_EMU_ENABLED
namespace LuaFunc {
static void load_symbols(const String& id, Loader::RuntimeLinker* rt)
{
EXIT_IF(rt == nullptr);
if (!Libs::Init(id, rt->Symbols()))
{
EXIT("Unknown library: %s\n", id.C_Str());
}
}
static void print_system_info()
{
Loader::SystemInfo info = Loader::GetSystemInfo();
printf("PageSize = %" PRIu32 "\n", info.PageSize);
printf("MinimumApplicationAddress = 0x%016" PRIx64 "\n", info.MinimumApplicationAddress);
printf("MaximumApplicationAddress = 0x%016" PRIx64 "\n", info.MaximumApplicationAddress);
printf("ActiveProcessorMask = 0x%08" PRIx32 "\n", info.ActiveProcessorMask);
printf("NumberOfProcessors = %" PRIu32 "\n", info.NumberOfProcessors);
printf("ProcessorArchitecture = %s\n", Core::EnumName(info.ProcessorArchitecture).C_Str());
printf("AllocationGranularity = %" PRIu32 "\n", info.AllocationGranularity);
printf("ProcessorLevel = %" PRIu16 "\n", info.ProcessorLevel);
printf("ProcessorRevision = 0x%04" PRIx16 "\n", info.ProcessorRevision);
}
static void kyty_close()
{
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
rt->Clear();
printf("done!\n");
Core::SubsystemsListSingleton::Instance()->ShutdownAll();
}
static void Init(const Scripts::ScriptVar& cfg)
{
EXIT_IF(!Core::Thread::IsMainThread());
auto* slist = Core::SubsystemsList::Instance();
auto* log = Log::LogSubsystem::Instance();
auto* core = Core::CoreSubsystem::Instance();
auto* scripts = Scripts::ScriptsSubsystem::Instance();
auto* config = Config::ConfigSubsystem::Instance();
auto* pthread = Libs::LibKernel::PthreadSubsystem::Instance();
auto* timer = Loader::Timer::TimerSubsystem::Instance();
auto* file_system = Libs::LibKernel::FileSystem::FileSystemSubsystem::Instance();
auto* memory = Libs::LibKernel::Memory::MemorySubsystem::Instance();
auto* graphics = Libs::Graphics::GraphicsSubsystem::Instance();
auto* profiler = Profiler::ProfilerSubsystem::Instance();
auto* controller = Libs::Controller::ControllerSubsystem::Instance();
slist->Add(config, {core, scripts});
slist->InitAll(true);
Config::Load(cfg);
slist->Add(log, {core, config});
slist->Add(pthread, {core, log, timer});
slist->Add(timer, {core, log});
slist->Add(memory, {core, log});
slist->Add(controller, {core, log, config});
slist->Add(file_system, {core, log, pthread});
slist->Add(graphics, {core, log, pthread, memory, config, profiler, controller});
slist->Add(profiler, {core, config});
slist->InitAll(true);
}
KYTY_SCRIPT_FUNC(kyty_init_func)
{
if (Scripts::ArgGetVarCount() != 1)
{
EXIT("invalid args\n");
}
Scripts::ScriptVar cfg = Scripts::ArgGetVar(0);
Init(cfg);
print_system_info();
atexit(kyty_close);
return 0;
}
KYTY_SCRIPT_FUNC(kyty_load_elf_func)
{
if (Scripts::ArgGetVarCount() != 1 && Scripts::ArgGetVarCount() != 2)
{
EXIT("invalid args\n");
}
Scripts::ScriptVar elf = Scripts::ArgGetVar(0);
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
auto* program = rt->LoadProgram(Libs::LibKernel::FileSystem::GetRealFilename(elf.ToString()));
if (Scripts::ArgGetVarCount() == 2)
{
if (Scripts::ArgGetVar(1).ToInteger() == 1)
{
program->dbg_print_reloc = true;
}
}
return 0;
}
KYTY_SCRIPT_FUNC(kyty_load_symbols_func)
{
auto count = Scripts::ArgGetVarCount();
if (count < 1)
{
EXIT("invalid args\n");
}
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
for (int i = 0; i < count; i++)
{
Scripts::ScriptVar id = Scripts::ArgGetVar(i);
load_symbols(id.ToString(), rt);
}
return 0;
}
KYTY_SCRIPT_FUNC(kyty_dbg_dump_func)
{
if (Scripts::ArgGetVarCount() != 1)
{
EXIT("invalid args\n");
}
Scripts::ScriptVar dbg_dir = Scripts::ArgGetVar(0);
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
rt->DbgDump(dbg_dir.ToString());
return 0;
}
KYTY_SCRIPT_FUNC(kyty_execute_func)
{
if (Scripts::ArgGetVarCount() != 0)
{
EXIT("invalid args\n");
}
int thread_model = 1;
if (thread_model == 0)
{
Core::Thread t([](void* /*unused*/) { Libs::Graphics::WindowRun(); }, nullptr);
t.Detach();
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
rt->Execute();
} else
{
Core::Thread t(
[](void* /*unused*/)
{
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
rt->Execute();
},
nullptr);
t.Detach();
Libs::Graphics::WindowRun();
t.Join();
}
return 0;
}
KYTY_SCRIPT_FUNC(kyty_mount_func)
{
if (Scripts::ArgGetVarCount() != 2)
{
EXIT("invalid args\n");
}
Scripts::ScriptVar folder = Scripts::ArgGetVar(0);
Scripts::ScriptVar point = Scripts::ArgGetVar(1);
Libs::LibKernel::FileSystem::Mount(folder.ToString(), point.ToString());
return 0;
}
KYTY_SCRIPT_FUNC(kyty_shader_disable)
{
if (Scripts::ArgGetVarCount() != 1)
{
EXIT("invalid args\n");
}
auto id = Scripts::ArgGetVar(0).ToString().ToUint64(16);
Libs::Graphics::ShaderDisable(id);
return 0;
}
void kyty_help() {}
} // namespace LuaFunc
void kyty_reg()
{
Scripts::RegisterFunc("kyty_init", LuaFunc::kyty_init_func, LuaFunc::kyty_help);
Scripts::RegisterFunc("kyty_load_elf", LuaFunc::kyty_load_elf_func, LuaFunc::kyty_help);
Scripts::RegisterFunc("kyty_load_symbols", LuaFunc::kyty_load_symbols_func, LuaFunc::kyty_help);
Scripts::RegisterFunc("kyty_dbg_dump", LuaFunc::kyty_dbg_dump_func, LuaFunc::kyty_help);
Scripts::RegisterFunc("kyty_execute", LuaFunc::kyty_execute_func, LuaFunc::kyty_help);
Scripts::RegisterFunc("kyty_mount", LuaFunc::kyty_mount_func, LuaFunc::kyty_help);
Scripts::RegisterFunc("kyty_shader_disable", LuaFunc::kyty_shader_disable, LuaFunc::kyty_help);
}
#else
void kyty_reg() {}
#endif // KYTY_EMU_ENABLED
} // namespace Kyty::Emulator
+219
View File
@@ -0,0 +1,219 @@
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/LinkList.h"
#include "Kyty/Core/Singleton.h"
#include "Kyty/Core/String.h"
#include "Emulator/Common.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/Libs/Printf.h"
#include "Emulator/Libs/VaContext.h"
#include "Emulator/SymbolDatabase.h"
#include <cstdlib>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
namespace LibC {
LIB_VERSION("libc", 1, "libc", 1, 1);
static uint32_t g_need_flag = 1;
using cxa_destructor_func_t = void (*)(void*);
struct CxaDestructor
{
cxa_destructor_func_t destructor_func;
void* destructor_object;
void* module_id;
};
struct CContext
{
Core::List<CxaDestructor> cxa;
};
static KYTY_SYSV_ABI void exit(int code)
{
PRINT_NAME();
::exit(code);
}
static KYTY_SYSV_ABI void init_env()
{
PRINT_NAME();
}
static KYTY_SYSV_ABI int atexit(void (*func)())
{
PRINT_NAME();
::printf("func = %" PRIx64 "\n", reinterpret_cast<uint64_t>(func));
::atexit(func);
return 0;
}
static KYTY_SYSV_ABI int printf(VA_ARGS)
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
VA_CONTEXT(ctx);
PRINT_NAME();
return GetPrintFuncV()(&ctx);
}
static KYTY_SYSV_ABI int puts(const char* s)
{
PRINT_NAME();
return GetPrintFunc()("%s\n", s);
}
static KYTY_SYSV_ABI void catchReturnFromMain(int status)
{
PRINT_NAME();
::printf("return from main = %d\n", status);
}
static KYTY_SYSV_ABI int cxa_atexit(void (*func)(void*), void* arg, void* d)
{
PRINT_NAME();
auto* cc = Core::Singleton<CContext>::Instance();
CxaDestructor c {};
c.destructor_func = func;
c.destructor_object = arg;
c.module_id = d;
cc->cxa.Add(c);
return 0;
}
void KYTY_SYSV_ABI cxa_finalize(void* d)
{
PRINT_NAME();
auto* cc = Core::Singleton<CContext>::Instance();
FOR_LIST_R(i, cc->cxa)
{
auto& c = cc->cxa[i];
if (c.module_id == d && c.destructor_func != nullptr)
{
c.destructor_func(c.destructor_object);
c.destructor_func = nullptr;
}
}
}
} // namespace LibC
namespace LibcInternalExt {
LIB_VERSION("LibcInternalExt", 1, "LibcInternal", 1, 1);
static uint64_t g_mspace_atomic_id_mask = 0;
static uint64_t g_mstate_table[64] = {0};
struct Info
{
uint64_t size;
uint32_t unknown1;
uint32_t unknown2;
uint64_t* mspace_atomic_id_mask;
uint64_t* mstate_table;
};
void KYTY_SYSV_ABI LibcHeapGetTraceInfo(Info* info)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(info->size != 32);
info->mspace_atomic_id_mask = &g_mspace_atomic_id_mask;
info->mstate_table = g_mstate_table;
}
LIB_DEFINE(InitLibcInternalExt_1)
{
LIB_FUNC("NWtTN10cJzE", LibcInternalExt::LibcHeapGetTraceInfo);
}
} // namespace LibcInternalExt
namespace LibcInternal {
LIB_VERSION("LibcInternal", 1, "LibcInternal", 1, 1);
static uint32_t g_need_flag = 1;
int KYTY_SYSV_ABI vprintf(const char* str, VaList* c)
{
PRINT_NAME();
return GetVPrintFunc()(str, c);
}
int KYTY_SYSV_ABI fflush(FILE* stream)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(stream != stdout);
return ::fflush(stream);
}
void* KYTY_SYSV_ABI memset(void* s, int c, size_t n)
{
PRINT_NAME();
return ::memset(s, c, n);
}
LIB_DEFINE(InitLibcInternal_1)
{
LibcInternalExt::InitLibcInternalExt_1(s);
LIB_OBJECT("ZT4ODD2Ts9o", &LibcInternal::g_need_flag);
LIB_OBJECT("2sWzhYqFH4E", stdout);
LIB_FUNC("GMpvxPFW924", LibcInternal::vprintf);
LIB_FUNC("MUjC4lbHrK4", LibcInternal::fflush);
LIB_FUNC("8zTFvBIAIN8", LibcInternal::memset);
LIB_FUNC("H2e8t5ScQGc", LibC::cxa_finalize);
}
} // namespace LibcInternal
LIB_USING(LibC);
LIB_DEFINE(InitLibC_1)
{
LibcInternal::InitLibcInternal_1(s);
LIB_OBJECT("P330P3dFF68", &LibC::g_need_flag);
LIB_FUNC("uMei1W9uyNo", LibC::exit);
LIB_FUNC("bzQExy189ZI", LibC::init_env);
LIB_FUNC("8G2LB+A3rzg", LibC::atexit);
LIB_FUNC("hcuQgD53UxM", LibC::printf);
LIB_FUNC("YQ0navp+YIc", LibC::puts);
LIB_FUNC("XKRegsFpEpk", LibC::catchReturnFromMain);
LIB_FUNC("tsvEmnenz48", LibC::cxa_atexit);
LIB_FUNC("H2e8t5ScQGc", LibC::cxa_finalize);
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
+37
View File
@@ -0,0 +1,37 @@
#include "Kyty/Core/Common.h"
#include "Kyty/Core/String.h"
#include "Emulator/Common.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/SymbolDatabase.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
namespace LibRazorCpu {
LIB_VERSION("RazorCpu", 1, "RazorCpu", 1, 1);
static KYTY_SYSV_ABI uint32_t RazorCpuIsCapturing()
{
PRINT_NAME();
return 0;
}
LIB_DEFINE(InitLibRazorCpu_1)
{
LIB_FUNC("EboejOQvLL4", LibRazorCpu::RazorCpuIsCapturing);
}
} // namespace LibRazorCpu
LIB_DEFINE(InitDebug_1)
{
LibRazorCpu::InitLibRazorCpu_1(s);
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
+52
View File
@@ -0,0 +1,52 @@
#include "Kyty/Core/Common.h"
#include "Kyty/Core/String.h"
#include "Emulator/Common.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/SymbolDatabase.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
LIB_VERSION("DiscMap", 1, "DiscMap", 1, 1);
namespace DiscMap {
static KYTY_SYSV_ABI int DiscMapIsRequestOnHDD(const char* file, uint64_t a2, uint64_t a3, const int* a4)
{
PRINT_NAME();
printf("\tfile = %s\n", file);
printf("\ta2 = %016" PRIx64 "\n", a2);
printf("\ta3 = %016" PRIx64 "\n", a3);
printf("\t*a4 = %08" PRIx32 "\n", *a4);
return 0;
}
static KYTY_SYSV_ABI int Unknown(const char* file, uint64_t a2, uint64_t a3, const uint64_t* a4, const uint64_t* a5, const uint64_t* a6)
{
PRINT_NAME();
printf("\tfile = %s\n", file);
printf("\ta2 = %016" PRIx64 "\n", a2);
printf("\ta3 = %016" PRIx64 "\n", a3);
printf("\t*a4 = %016" PRIx64 "\n", *a4);
printf("\t*a5 = %016" PRIx64 "\n", *a5);
printf("\t*a6 = %016" PRIx64 "\n", *a6);
return 0;
}
} // namespace DiscMap
LIB_DEFINE(InitDiscMap_1)
{
LIB_FUNC("lbQKqsERhtE", DiscMap::DiscMapIsRequestOnHDD);
LIB_FUNC("fJgP+wqifno", DiscMap::Unknown);
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
@@ -0,0 +1,45 @@
#include "Emulator/Common.h"
#include "Emulator/Graphics/Graphics.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/SymbolDatabase.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
LIB_VERSION("GraphicsDriver", 1, "GraphicsDriver", 1, 1);
LIB_DEFINE(InitGraphicsDriver_1)
{
PRINT_NAME_ENABLE(true);
LIB_FUNC("gAhCn6UiU4Y", Graphics::GraphicsSetVsShader);
LIB_FUNC("5uFKckiJYRM", Graphics::GraphicsSetPsShader350);
LIB_FUNC("Kx-h-nWQJ8A", Graphics::GraphicsSetCsShaderWithModifier);
LIB_FUNC("HlTPoZ-oY7Y", Graphics::GraphicsDrawIndex);
LIB_FUNC("GGsn7jMTxw4", Graphics::GraphicsDrawIndexAuto);
LIB_FUNC("zwY0YV91TTI", Graphics::GraphicsSubmitCommandBuffers);
LIB_FUNC("xbxNatawohc", Graphics::GraphicsSubmitAndFlipCommandBuffers);
LIB_FUNC("yvZ73uQUqrk", Graphics::GraphicsSubmitDone);
LIB_FUNC("iBt3Oe00Kvc", Graphics::GraphicsFlushMemory);
LIB_FUNC("b0xyllnVY-I", Graphics::GraphicsAddEqEvent);
LIB_FUNC("PVT+fuoS9gU", Graphics::GraphicsDeleteEqEvent);
LIB_FUNC("yb2cRhagD1I", Graphics::GraphicsDrawInitDefaultHardwareState350);
LIB_FUNC("nF6bFRUBRAU", Graphics::GraphicsDispatchInitDefaultHardwareState);
LIB_FUNC("1qXLHIpROPE", Graphics::GraphicsInsertWaitFlipDone);
LIB_FUNC("0BzLGljcwBo", Graphics::GraphicsDispatchDirect);
LIB_FUNC("29oKvKXzEZo", Graphics::GraphicsMapComputeQueue);
LIB_FUNC("ArSg-TGinhk", Graphics::GraphicsUnmapComputeQueue);
LIB_FUNC("ffrNQOshows", Graphics::GraphicsComputeWaitOnAddress);
LIB_FUNC("bX5IbRvECXk", Graphics::GraphicsDingDong);
LIB_FUNC("W1Etj-jlW7Y", Graphics::GraphicsInsertPushMarker);
LIB_FUNC("7qZVNgEu+SY", Graphics::GraphicsInsertPopMarker);
LIB_FUNC("+AFvOEXrKJk", Graphics::GraphicsSetEmbeddedVsShader);
LIB_FUNC("ZFqKFl23aMc", Graphics::GraphicsRegisterOwner);
LIB_FUNC("nvEwfYAImTs", Graphics::GraphicsRegisterResource);
LIB_FUNC("Fwvh++m9IQI", Graphics::GraphicsGetGpuCoreClockFrequency);
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
+560
View File
@@ -0,0 +1,560 @@
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Singleton.h"
#include "Kyty/Core/String.h"
#include "Kyty/Math/Rand.h"
#include "Emulator/Common.h"
#include "Emulator/Config.h"
#include "Emulator/Kernel/EventFlag.h"
#include "Emulator/Kernel/EventQueue.h"
#include "Emulator/Kernel/FileSystem.h"
#include "Emulator/Kernel/Memory.h"
#include "Emulator/Kernel/Pthread.h"
#include "Emulator/Libs/Errno.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/RuntimeLinker.h"
#include "Emulator/SymbolDatabase.h"
#include <cstdlib>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
LIB_VERSION("libkernel", 1, "libkernel", 1, 1);
namespace LibKernel {
using KernelModule = int32_t;
using get_thread_atexit_count_func_t = KYTY_SYSV_ABI int (*)(KernelModule);
using thread_atexit_report_func_t = KYTY_SYSV_ABI void (*)(KernelModule);
#pragma pack(1)
struct KernelLoadModuleOpt
{
size_t size;
};
struct KernelUnloadModuleOpt
{
size_t size;
};
struct TlsInfo
{
Loader::Program* program;
uint64_t offset;
};
struct MallocReplace
{
uint64_t size = sizeof(MallocReplace);
void* malloc_initialize = nullptr;
void* malloc_finalize = nullptr;
void* malloc = nullptr;
void* free = nullptr;
void* calloc = nullptr;
void* realloc = nullptr;
void* memalign = nullptr;
void* reallocalign = nullptr;
void* posix_memalign = nullptr;
void* malloc_stats = nullptr;
void* malloc_stats_fast = nullptr;
void* malloc_usable_size = nullptr;
void* aligned_alloc = nullptr;
};
struct NewReplace
{
uint64_t size = sizeof(NewReplace);
void* new_p = nullptr;
void* new_nothrow = nullptr;
void* new_array = nullptr;
void* new_array_nothrow = nullptr;
void* delete_p = nullptr;
void* delete_nothrow = nullptr;
void* delete_array = nullptr;
void* delete_array_nothrow = nullptr;
void* delete_with_size = nullptr;
void* delete_with_size_nothrow = nullptr;
void* delete_array_with_size = nullptr;
void* delete_array_with_size_nothrow = nullptr;
};
struct ModuleInfo
{
uint64_t size;
uint64_t info[32];
KernelModule handle;
uint8_t pad[156];
};
#pragma pack()
constexpr size_t PROGNAME_MAX_SIZE = 511;
static uint64_t g_stack_chk_guard = 0xDeadBeef5533CCAA;
static char g_progname_buf[PROGNAME_MAX_SIZE + 1] = {0};
static const char* g_progname = g_progname_buf;
static get_thread_atexit_count_func_t g_get_thread_atexit_count_func = nullptr;
static thread_atexit_report_func_t g_thread_atexit_report_func = nullptr;
static thread_local int g_errno = 0;
void SetProgName(const String& name)
{
strncpy(g_progname_buf, name.C_Str(), PROGNAME_MAX_SIZE);
}
// struct KernelContext
//{
// Vector<Loader::Program*> programs;
//};
static KYTY_SYSV_ABI int* get_error_addr()
{
PRINT_NAME();
return &g_errno;
}
static KYTY_SYSV_ABI void stack_chk_fail()
{
PRINT_NAME();
EXIT("stack fail!!!");
}
static KYTY_SYSV_ABI KernelModule KernelLoadStartModule(const char* module_file_name, size_t args, const void* argp, uint32_t flags,
const KernelLoadModuleOpt* opt, int* res)
{
PRINT_NAME();
// EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
printf("\tmodule_file_name = %s\n", module_file_name);
EXIT_NOT_IMPLEMENTED(flags != 0);
EXIT_NOT_IMPLEMENTED(opt != nullptr);
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
auto* program = rt->LoadProgram(FileSystem::GetRealFilename(String::FromUtf8(module_file_name)));
auto handle = program->unique_id;
program->dbg_print_reloc = true;
rt->RelocateAll();
int result = rt->StartModule(program, args, argp, nullptr);
printf("\tmodule_start() result = %d\n", result);
EXIT_NOT_IMPLEMENTED(result < 0);
if (res != nullptr)
{
*res = result;
}
return static_cast<KernelModule>(handle);
}
static int KYTY_SYSV_ABI KernelStopUnloadModule(KernelModule handle, size_t args, const void* argp, uint32_t flags,
const KernelUnloadModuleOpt* opt, int* res)
{
PRINT_NAME();
// EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
EXIT_NOT_IMPLEMENTED(flags != 0);
EXIT_NOT_IMPLEMENTED(opt != nullptr);
auto* program = rt->FindProgramById(handle);
EXIT_NOT_IMPLEMENTED(program == nullptr);
if (g_get_thread_atexit_count_func != nullptr && g_get_thread_atexit_count_func(program->unique_id) > 0)
{
printf("KernelStopUnloadModule: cannot unload %s\n", program->file_name.C_Str());
if (g_thread_atexit_report_func != nullptr)
{
g_thread_atexit_report_func(program->unique_id);
}
return KERNEL_ERROR_EBUSY;
}
int result = rt->StopModule(program, args, argp, nullptr);
printf("\tmodule_stop() result = %d\n", result);
EXIT_NOT_IMPLEMENTED(result < 0);
if (res != nullptr)
{
*res = result;
}
rt->UnloadProgram(program);
return OK;
}
static void* KYTY_SYSV_ABI tls_get_addr(TlsInfo* info)
{
PRINT_NAME();
// EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
return Loader::RuntimeLinker::TlsGetAddr(info->program) + info->offset;
}
static void* KYTY_SYSV_ABI KernelGetProcParam()
{
PRINT_NAME();
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
return reinterpret_cast<void*>(rt->GetProcParam());
}
static void KYTY_SYSV_ABI KernelRtldSetApplicationHeapAPI(void* api[])
{
PRINT_NAME();
for (int i = 0; i < 10; i++)
{
printf("\tapi[%d] = 0x%016" PRIx64 "\n", i, reinterpret_cast<uint64_t>(api[i]));
}
[[maybe_unused]] auto* heap_malloc = api[0];
[[maybe_unused]] auto* heap_free = api[1];
[[maybe_unused]] auto* heap_posix_memalign = api[6];
}
static int KYTY_SYSV_ABI write(int d, const char* str, int64_t size)
{
// PRINT_NAME();
EXIT_NOT_IMPLEMENTED(d < 0 || d > 2);
int size_int = static_cast<int>(size);
printf(FG_BRIGHT_MAGENTA "%.*s" DEFAULT, size_int, str);
return size_int;
}
static int KYTY_SYSV_ABI KernelGetModuleInfoFromAddr(uint64_t addr, int n, ModuleInfo* r)
{
PRINT_NAME();
printf("\taddr = %016" PRIx64 "\n", addr);
printf("\tn = %d\n", n);
EXIT_NOT_IMPLEMENTED(n != 2);
EXIT_NOT_IMPLEMENTED(r == nullptr);
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
auto* p = rt->FindProgramByAddr(addr);
if (p == nullptr)
{
printf("\thandle: not found\n");
r->handle = 0;
return -1;
}
r->handle = p->unique_id;
printf("\thandle: %d\n", r->handle);
return 0;
}
static void KYTY_SYSV_ABI KernelDebugRaiseExceptionOnReleaseMode(int /*c1*/, int /*c2*/)
{
PRINT_NAME();
}
static void KYTY_SYSV_ABI KernelDebugRaiseException(int /*c1*/, int /*c2*/)
{
PRINT_NAME();
}
static void KYTY_SYSV_ABI exit(int code)
{
PRINT_NAME();
::exit(code);
}
static KYTY_SYSV_ABI MallocReplace* KernelGetSanitizerMallocReplaceExternal()
{
PRINT_NAME();
static MallocReplace ret;
return &ret;
}
static KYTY_SYSV_ABI NewReplace* KernelGetSanitizerNewReplaceExternal()
{
PRINT_NAME();
static NewReplace ret;
return &ret;
}
static KYTY_SYSV_ABI int elf_phdr_match_addr(ModuleInfo* m, uint64_t dtor_vaddr)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(m == nullptr);
auto* rt = Core::Singleton<Loader::RuntimeLinker>::Instance();
auto* p = rt->FindProgramByAddr(dtor_vaddr);
int result = (p != nullptr && p->unique_id == m->handle) ? 1 : 0;
printf("\thandle = %" PRId32 "\n", m->handle);
printf("\tdtor_vaddr = %016" PRIx64 "\n", dtor_vaddr);
printf("\tmatch = %s\n", result == 1 ? "true" : "false");
return result;
}
int KYTY_SYSV_ABI KernelUuidCreate(uint32_t* uuid)
{
PRINT_NAME();
if (uuid == nullptr)
{
return KERNEL_ERROR_EINVAL;
}
uuid[0] = Kyty::Math::Rand::Uint();
uuid[1] = Kyty::Math::Rand::Uint();
uuid[2] = Kyty::Math::Rand::Uint();
uuid[3] = Kyty::Math::Rand::Uint();
return OK;
}
static KYTY_SYSV_ABI void pthread_cxa_finalize(void* /*p*/)
{
PRINT_NAME();
}
void KYTY_SYSV_ABI KernelSetThreadAtexitCount(get_thread_atexit_count_func_t func)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(g_get_thread_atexit_count_func != nullptr);
g_get_thread_atexit_count_func = func;
}
void KYTY_SYSV_ABI KernelSetThreadAtexitReport(thread_atexit_report_func_t func)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(g_thread_atexit_report_func != nullptr);
g_thread_atexit_report_func = func;
}
int KYTY_SYSV_ABI KernelRtldThreadAtexitIncrement(uint64_t* /*c*/)
{
PRINT_NAME();
//__sync_fetch_and_add(c, 1);
return 0;
}
int KYTY_SYSV_ABI KernelRtldThreadAtexitDecrement(uint64_t* /*c*/)
{
PRINT_NAME();
//__sync_fetch_and_sub(c, 1);
return 0;
}
int KYTY_SYSV_ABI KernelIsNeoMode()
{
PRINT_NAME();
return (Config::IsNeo() ? 1 : 0);
}
} // namespace LibKernel
namespace Posix {
LIB_VERSION("Posix", 1, "libkernel", 1, 1);
int KYTY_SYSV_ABI clock_gettime(int clock_id, LibKernel::KernelTimespec* time)
{
PRINT_NAME();
if (LibKernel::KernelClockGettime(clock_id, time) < 0)
{
return -1;
}
return 0;
}
LIB_DEFINE(InitLibKernel_1_Posix)
{
LIB_FUNC("lLMT9vJAck0", clock_gettime);
}
} // namespace Posix
namespace FileSystem = LibKernel::FileSystem;
namespace Memory = LibKernel::Memory;
namespace EventQueue = LibKernel::EventQueue;
namespace EventFlag = LibKernel::EventFlag;
LIB_DEFINE(InitLibKernel_1_FS)
{
LIB_FUNC("1G3lF1Gg1k8", FileSystem::KernelOpen);
LIB_FUNC("UK2Tl2DWUns", FileSystem::KernelClose);
LIB_FUNC("Cg4srZ6TKbU", FileSystem::KernelRead);
LIB_FUNC("4wSze92BhLI", FileSystem::KernelWrite);
LIB_FUNC("+r3rMFwItV4", FileSystem::KernelPread);
LIB_FUNC("nKWi-N2HBV4", FileSystem::KernelPwrite);
LIB_FUNC("eV9wAD2riIA", FileSystem::KernelStat);
LIB_FUNC("kBwCPsYX-m4", FileSystem::KernelFstat);
LIB_FUNC("AUXVxWeJU-A", FileSystem::KernelUnlink);
LIB_FUNC("taRWhTJFTgE", FileSystem::KernelGetdirentries);
LIB_FUNC("oib76F-12fk", FileSystem::KernelLseek);
}
LIB_DEFINE(InitLibKernel_1_Mem)
{
LIB_FUNC("mL8NDH86iQI", Memory::KernelMapNamedFlexibleMemory);
LIB_FUNC("cQke9UuBQOk", Memory::KernelMunmap);
LIB_FUNC("pO96TwzOm5E", Memory::KernelGetDirectMemorySize);
LIB_FUNC("rTXw65xmLIA", Memory::KernelAllocateDirectMemory);
LIB_FUNC("L-Q3LEjIbgA", Memory::KernelMapDirectMemory);
LIB_FUNC("MBuItvba6z8", Memory::KernelReleaseDirectMemory);
LIB_FUNC("WFcfL2lzido", Memory::KernelQueryMemoryProtection);
}
LIB_DEFINE(InitLibKernel_1_Equeue)
{
LIB_FUNC("D0OdFMjp46I", EventQueue::KernelCreateEqueue);
LIB_FUNC("jpFjmgAC5AE", EventQueue::KernelDeleteEqueue);
LIB_FUNC("fzyMKs9kim0", EventQueue::KernelWaitEqueue);
}
LIB_DEFINE(InitLibKernel_1_EventFlag)
{
LIB_FUNC("BpFoboUJoZU", EventFlag::KernelCreateEventFlag);
LIB_FUNC("JTvBflhYazQ", EventFlag::KernelWaitEventFlag);
}
LIB_DEFINE(InitLibKernel_1_Pthread)
{
LIB_FUNC("9UK1vLZQft4", LibKernel::PthreadMutexLock);
LIB_FUNC("tn3VlD0hG60", LibKernel::PthreadMutexUnlock);
LIB_FUNC("2Of0f+3mhhE", LibKernel::PthreadMutexDestroy);
LIB_FUNC("cmo1RIYva9o", LibKernel::PthreadMutexInit);
LIB_FUNC("upoVrzMHFeE", LibKernel::PthreadMutexTrylock);
LIB_FUNC("smWEktiyyG0", LibKernel::PthreadMutexattrDestroy);
LIB_FUNC("F8bUHwAG284", LibKernel::PthreadMutexattrInit);
LIB_FUNC("iMp8QpE+XO4", LibKernel::PthreadMutexattrSettype);
LIB_FUNC("1FGvU0i9saQ", LibKernel::PthreadMutexattrSetprotocol);
LIB_FUNC("aI+OeCz8xrQ", LibKernel::PthreadSelf);
LIB_FUNC("6UgtwV+0zb4", LibKernel::PthreadCreate);
LIB_FUNC("3PtV6p3QNX4", LibKernel::PthreadEqual);
LIB_FUNC("onNY9Byn-W8", LibKernel::PthreadJoin);
LIB_FUNC("How7B8Oet6k", LibKernel::PthreadGetname);
LIB_FUNC("62KCwEMmzcM", LibKernel::PthreadAttrDestroy);
LIB_FUNC("x1X76arYMxU", LibKernel::PthreadAttrGet);
LIB_FUNC("8+s5BzZjxSg", LibKernel::PthreadAttrGetaffinity);
LIB_FUNC("nsYoNRywwNg", LibKernel::PthreadAttrInit);
LIB_FUNC("JaRMy+QcpeU", LibKernel::PthreadAttrGetdetachstate);
LIB_FUNC("UTXzJbWhhTE", LibKernel::PthreadAttrSetstacksize);
LIB_FUNC("-Wreprtu0Qs", LibKernel::PthreadAttrSetdetachstate);
LIB_FUNC("eXbUSpEaTsA", LibKernel::PthreadAttrSetinheritsched);
LIB_FUNC("DzES9hQF4f4", LibKernel::PthreadAttrSetschedparam);
LIB_FUNC("4+h9EzwKF4I", LibKernel::PthreadAttrSetschedpolicy);
LIB_FUNC("6ULAa0fq4jA", LibKernel::PthreadRwlockInit);
LIB_FUNC("BB+kb08Tl9A", LibKernel::PthreadRwlockDestroy);
LIB_FUNC("Ox9i0c7L5w0", LibKernel::PthreadRwlockRdlock);
LIB_FUNC("+L98PIbGttk", LibKernel::PthreadRwlockUnlock);
LIB_FUNC("mqdNorrB+gI", LibKernel::PthreadRwlockWrlock);
LIB_FUNC("2Tb92quprl0", LibKernel::PthreadCondInit);
LIB_FUNC("g+PZd2hiacg", LibKernel::PthreadCondDestroy);
LIB_FUNC("WKAXJ4XBPQ4", LibKernel::PthreadCondWait);
LIB_FUNC("JGgj7Uvrl+A", LibKernel::PthreadCondBroadcast);
LIB_FUNC("BmMjYxmew1w", LibKernel::PthreadCondTimedwait);
LIB_FUNC("QBi7HCK03hw", LibKernel::KernelClockGettime);
LIB_FUNC("ejekcaNQNq0", LibKernel::KernelGettimeofday);
LIB_FUNC("1j3S3n-tTW4", LibKernel::KernelGetTscFrequency);
LIB_FUNC("7H0iTOciTLo", LibKernel::pthread_mutex_lock_s);
LIB_FUNC("2Z+PpY6CaJg", LibKernel::pthread_mutex_unlock_s);
LIB_FUNC("mkx2fVhNMsg", LibKernel::pthread_cond_broadcast_s);
LIB_FUNC("Op8TBGY5KHg", LibKernel::pthread_cond_wait_s);
}
LIB_DEFINE(InitLibKernel_1)
{
InitLibKernel_1_FS(s);
InitLibKernel_1_Mem(s);
InitLibKernel_1_Equeue(s);
InitLibKernel_1_EventFlag(s);
InitLibKernel_1_Pthread(s);
Posix::InitLibKernel_1_Posix(s);
LIB_OBJECT("f7uOxY9mM1U", &LibKernel::g_stack_chk_guard);
LIB_OBJECT("djxxOmW6-aw", &LibKernel::g_progname);
LIB_FUNC("Ou3iL1abvng", LibKernel::stack_chk_fail);
LIB_FUNC("wzvqT4UqKX8", LibKernel::KernelLoadStartModule);
LIB_FUNC("QKd0qM58Qes", LibKernel::KernelStopUnloadModule);
LIB_FUNC("vNe1w4diLCs", LibKernel::tls_get_addr);
LIB_FUNC("959qrazPIrg", LibKernel::KernelGetProcParam);
LIB_FUNC("p5EcQeEeJAE", LibKernel::KernelRtldSetApplicationHeapAPI);
LIB_FUNC("FxVZqBAA7ks", LibKernel::write);
LIB_FUNC("f7KBOafysXo", LibKernel::KernelGetModuleInfoFromAddr);
LIB_FUNC("zE-wXIZjLoM", LibKernel::KernelDebugRaiseExceptionOnReleaseMode);
LIB_FUNC("OMDRKKAZ8I4", LibKernel::KernelDebugRaiseException);
LIB_FUNC("6Z83sYWFlA8", LibKernel::exit);
LIB_FUNC("py6L8jiVAN8", LibKernel::KernelGetSanitizerMallocReplaceExternal);
LIB_FUNC("bnZxYgAFeA0", LibKernel::KernelGetSanitizerNewReplaceExternal);
LIB_FUNC("Fjc4-n1+y2g", LibKernel::elf_phdr_match_addr);
LIB_FUNC("kbw4UHHSYy0", LibKernel::pthread_cxa_finalize);
LIB_FUNC("Xjoosiw+XPI", LibKernel::KernelUuidCreate);
LIB_FUNC("WslcK1FQcGI", LibKernel::KernelIsNeoMode);
LIB_FUNC("9BcDykPmo1I", LibKernel::get_error_addr);
LIB_FUNC("1jfXLRVzisc", LibKernel::KernelUsleep);
LIB_FUNC("rNhWz+lvOMU", LibKernel::KernelSetThreadDtors);
LIB_FUNC("WhCc1w3EhSI", LibKernel::KernelSetThreadAtexitReport);
LIB_FUNC("pB-yGZ2nQ9o", LibKernel::KernelSetThreadAtexitCount);
LIB_FUNC("Tz4RNUCBbGI", LibKernel::KernelRtldThreadAtexitIncrement);
LIB_FUNC("8OnWXlgQlvo", LibKernel::KernelRtldThreadAtexitDecrement);
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
+25
View File
@@ -0,0 +1,25 @@
#include "Emulator/Common.h"
#include "Emulator/Controller.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/SymbolDatabase.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
LIB_VERSION("Pad", 1, "Pad", 1, 1);
LIB_DEFINE(InitPad_1)
{
PRINT_NAME_ENABLE(true);
LIB_FUNC("hv1luiJrqQM", Controller::PadInit);
LIB_FUNC("xk0AcarP3V4", Controller::PadOpen);
LIB_FUNC("clVvL4ZDntw", Controller::PadSetMotionSensorState);
LIB_FUNC("gjP9-KQzoUk", Controller::PadGetControllerInformation);
LIB_FUNC("YndgXqQVV7c", Controller::PadReadState);
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
+62
View File
@@ -0,0 +1,62 @@
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/String.h"
#include "Emulator/Common.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/SymbolDatabase.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
LIB_VERSION("Sysmodule", 1, "Sysmodule", 1, 1);
namespace Sysmodule {
static KYTY_SYSV_ABI int SysmoduleLoadModule(uint16_t id)
{
PRINT_NAME();
printf("\tid = %d\n", static_cast<int>(id));
return 0;
}
static KYTY_SYSV_ABI int SysmoduleUnloadModule(uint16_t id)
{
PRINT_NAME();
printf("\tid = %d\n", static_cast<int>(id));
return 0;
}
static KYTY_SYSV_ABI int SysmoduleLoadModuleInternalWithArg(uint16_t id, int arg1, int arg2, int arg3, int* ret)
{
PRINT_NAME();
printf("\tid = %d\n", static_cast<int>(id));
EXIT_IF(arg1 != 0);
EXIT_IF(arg2 != 0);
EXIT_IF(arg3 != 0);
EXIT_IF(ret == nullptr);
*ret = 0;
return 0;
}
} // namespace Sysmodule
LIB_DEFINE(InitSysmodule_1)
{
LIB_FUNC("eR2bZFAAU0Q", Sysmodule::SysmoduleUnloadModule);
LIB_FUNC("hHrGoGoNf+s", Sysmodule::SysmoduleLoadModuleInternalWithArg);
LIB_FUNC("g8cM39EUZ6o", Sysmodule::SysmoduleLoadModule);
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
@@ -0,0 +1,44 @@
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/String.h"
#include "Emulator/Common.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/SymbolDatabase.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
LIB_VERSION("UserService", 1, "UserService", 1, 1);
namespace UserService {
static KYTY_SYSV_ABI int UserServiceInitialize(const void* /*params*/)
{
PRINT_NAME();
return 0;
}
static KYTY_SYSV_ABI int UserServiceGetInitialUser(int* user_id)
{
PRINT_NAME();
EXIT_NOT_IMPLEMENTED(user_id == nullptr);
*user_id = 1;
return 0;
}
} // namespace UserService
LIB_DEFINE(InitUserService_1)
{
LIB_FUNC("j3YMu1MVNNo", UserService::UserServiceInitialize);
LIB_FUNC("CdWp0oHWGr0", UserService::UserServiceGetInitialUser);
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
+29
View File
@@ -0,0 +1,29 @@
#include "Emulator/Common.h"
#include "Emulator/Graphics/VideoOut.h"
#include "Emulator/Libs/Libs.h"
#include "Emulator/SymbolDatabase.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
LIB_VERSION("VideoOut", 1, "VideoOut", 0, 0);
LIB_DEFINE(InitVideoOut_1)
{
PRINT_NAME_ENABLE(true);
LIB_FUNC("Up36PTk687E", VideoOut::VideoOutOpen);
LIB_FUNC("uquVH4-Du78", VideoOut::VideoOutClose);
LIB_FUNC("6kPnj51T62Y", VideoOut::VideoOutGetResolutionStatus);
LIB_FUNC("i6-sR91Wt-4", VideoOut::VideoOutSetBufferAttribute);
LIB_FUNC("CBiu4mCE1DA", VideoOut::VideoOutSetFlipRate);
LIB_FUNC("HXzjK9yI30k", VideoOut::VideoOutAddFlipEvent);
LIB_FUNC("w3BY+tAEiQY", VideoOut::VideoOutRegisterBuffers);
LIB_FUNC("U46NwOiJpys", VideoOut::VideoOutSubmitFlip);
LIB_FUNC("SbU3dwp80lQ", VideoOut::VideoOutGetFlipStatus);
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
+39
View File
@@ -0,0 +1,39 @@
#include "Emulator/Libs/Libs.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
namespace LibcInternal {
LIB_DEFINE(InitLibcInternal_1);
} // namespace LibcInternal
LIB_DEFINE(InitLibC_1);
LIB_DEFINE(InitLibKernel_1);
LIB_DEFINE(InitVideoOut_1);
LIB_DEFINE(InitSysmodule_1);
LIB_DEFINE(InitDiscMap_1);
LIB_DEFINE(InitDebug_1);
LIB_DEFINE(InitGraphicsDriver_1);
LIB_DEFINE(InitUserService_1);
LIB_DEFINE(InitPad_1);
bool Init(const String& id, Loader::SymbolDatabase* s)
{
LIB_CHECK(U"libc_1", InitLibC_1);
LIB_CHECK(U"libc_internal_1", LibcInternal::InitLibcInternal_1);
LIB_CHECK(U"libkernel_1", InitLibKernel_1);
LIB_CHECK(U"libVideoOut_1", InitVideoOut_1);
LIB_CHECK(U"libSysmodule_1", InitSysmodule_1);
LIB_CHECK(U"libDiscMap_1", InitDiscMap_1);
LIB_CHECK(U"libDebug_1", InitDebug_1);
LIB_CHECK(U"libGraphicsDriver_1", InitGraphicsDriver_1);
LIB_CHECK(U"libUserService_1", InitUserService_1);
LIB_CHECK(U"libPad_1", InitPad_1);
return false;
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
+895
View File
@@ -0,0 +1,895 @@
//
// Original algorithm is from:
// https://github.com/mpaland/printf
// Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
// licensed under The MIT License (MIT)
#include "Emulator/Libs/Printf.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Common.h"
#include "Emulator/Libs/VaContext.h"
#include <cfloat>
#include <cmath>
#include <cstddef>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs {
constexpr uint32_t FLAGS_ZEROPAD = (1U << 0U);
constexpr uint32_t FLAGS_LEFT = (1U << 1U);
constexpr uint32_t FLAGS_PLUS = (1U << 2U);
constexpr uint32_t FLAGS_SPACE = (1U << 3U);
constexpr uint32_t FLAGS_HASH = (1U << 4U);
constexpr uint32_t FLAGS_UPPERCASE = (1U << 5U);
constexpr uint32_t FLAGS_CHAR = (1U << 6U);
constexpr uint32_t FLAGS_SHORT = (1U << 7U);
constexpr uint32_t FLAGS_LONG = (1U << 8U);
constexpr uint32_t FLAGS_LONG_LONG = (1U << 9U);
constexpr uint32_t FLAGS_PRECISION = (1U << 10U);
constexpr uint32_t FLAGS_ADAPT_EXP = (1U << 11U);
constexpr size_t PRINTF_NTOA_BUFFER_SIZE = 32U;
constexpr size_t PRINTF_FTOA_BUFFER_SIZE = 32U;
constexpr double PRINTF_MAX_FLOAT = 1e9;
constexpr uint32_t PRINTF_DEFAULT_FLOAT_PRECISION = 6U;
using out_fct_type = void (*)(char character, Vector<char>* buffer, size_t idx, size_t /*maxlen*/);
// internal null output
static inline void _out_null(char character, Vector<char>* buffer, size_t /*idx*/, size_t /*maxlen*/)
{
buffer->Add(character);
}
static inline bool _is_digit(char ch)
{
return (ch >= '0') && (ch <= '9');
}
static unsigned int _atoi(const char** str)
{
unsigned int i = 0U;
while (_is_digit(**str))
{
i = i * 10U + static_cast<unsigned int>(*((*str)++) - '0');
}
return i;
}
static size_t _out_rev(out_fct_type out, Vector<char>* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width,
unsigned int flags)
{
const size_t start_idx = idx;
// pad spaces up to given width
if ((flags & FLAGS_LEFT) == 0 && (flags & FLAGS_ZEROPAD) == 0)
{
for (size_t i = len; i < width; i++)
{
out(' ', buffer, idx++, maxlen);
}
}
// reverse string
while (len != 0u)
{
out(buf[--len], buffer, idx++, maxlen);
}
// append pad spaces up to given width
if ((flags & FLAGS_LEFT) != 0u)
{
while (idx - start_idx < width)
{
out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
// internal itoa format
static size_t _ntoa_format(out_fct_type out, Vector<char>* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative,
unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
{
// pad leading zeros
if ((flags & FLAGS_LEFT) == 0u)
{
if ((width != 0u) && ((flags & FLAGS_ZEROPAD) != 0u) && (negative || ((flags & (FLAGS_PLUS | FLAGS_SPACE)) != 0u)))
{
width--;
}
while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE))
{
buf[len++] = '0';
}
while (((flags & FLAGS_ZEROPAD) != 0u) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE))
{
buf[len++] = '0';
}
}
// handle hash
if ((flags & FLAGS_HASH) != 0u)
{
if (((flags & FLAGS_PRECISION) == 0u) && (len != 0u) && ((len == prec) || (len == width)))
{
len--;
if ((len != 0u) && (base == 16U))
{
len--;
}
}
if ((base == 16U) && ((flags & FLAGS_UPPERCASE) == 0u) && (len < PRINTF_NTOA_BUFFER_SIZE))
{
buf[len++] = 'x';
} else if ((base == 16U) && ((flags & FLAGS_UPPERCASE) != 0u) && (len < PRINTF_NTOA_BUFFER_SIZE))
{
buf[len++] = 'X';
} else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE))
{
buf[len++] = 'b';
}
if (len < PRINTF_NTOA_BUFFER_SIZE)
{
buf[len++] = '0';
}
}
if (len < PRINTF_NTOA_BUFFER_SIZE)
{
if (negative)
{
buf[len++] = '-';
} else if ((flags & FLAGS_PLUS) != 0u)
{
buf[len++] = '+'; // ignore the space if the '+' exists
} else if ((flags & FLAGS_SPACE) != 0u)
{
buf[len++] = ' ';
}
}
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
}
static size_t _ntoa_long_long(out_fct_type out, Vector<char>* buffer, size_t idx, size_t maxlen, uint64_t value, bool negative,
uint64_t base, unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if (value == 0u)
{
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if (((flags & FLAGS_PRECISION) == 0u) || (value != 0u))
{
do
{
const char digit = static_cast<char>(value % base);
// NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
buf[len++] = digit < 10 ? '0' + digit : ((flags & FLAGS_UPPERCASE) != 0u ? 'A' : 'a') + digit - 10;
value /= base;
} while ((value != 0u) && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, static_cast<unsigned int>(base), prec, width, flags);
}
// internal itoa for 'long' type
static size_t _ntoa_long(out_fct_type out, Vector<char>* buffer, size_t idx, size_t maxlen, uint32_t value, bool negative, uint32_t base,
unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if (value == 0u)
{
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if (((flags & FLAGS_PRECISION) == 0u) || (value != 0u))
{
do
{
char digit = static_cast<char>(value % base);
// NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
buf[len++] = digit < 10 ? '0' + digit : ((flags & FLAGS_UPPERCASE) != 0u ? 'A' : 'a') + digit - 10;
value /= base;
} while ((value != 0u) && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, static_cast<unsigned int>(base), prec, width, flags);
}
static size_t _etoa(out_fct_type out, Vector<char>* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width,
unsigned int flags);
// internal ftoa for fixed decimal floating point
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
static size_t _ftoa(out_fct_type out, Vector<char>* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width,
unsigned int flags)
{
char buf[PRINTF_FTOA_BUFFER_SIZE];
size_t len = 0U;
double diff = 0.0;
// powers of 10
static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
// test for special values
if (value != value)
{
return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
}
if (value < -DBL_MAX)
{
return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
}
if (value > DBL_MAX)
{
return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) != 0u ? "fni+" : "fni", (flags & FLAGS_PLUS) != 0u ? 4U : 3U, width,
flags);
}
// test for very large values
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT))
{
return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
}
// test for negative
bool negative = false;
if (value < 0)
{
negative = true;
value = 0 - value;
}
// set default precision, if not set explicitly
if ((flags & FLAGS_PRECISION) == 0u)
{
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U))
{
buf[len++] = '0';
prec--;
}
int whole = static_cast<int>(value);
double tmp = (value - whole) * pow10[prec];
auto frac = static_cast<uint32_t>(tmp);
diff = tmp - frac;
if (diff > 0.5)
{
++frac;
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
if (frac >= pow10[prec])
{
frac = 0;
++whole;
}
} else if (diff < 0.5)
{
} else if ((frac == 0U) || ((frac & 1U) != 0u))
{
// if halfway, round up if odd OR if last digit is 0
++frac;
}
if (prec == 0U)
{
diff = value - static_cast<double>(whole);
if ((!(diff < 0.5) || (diff > 0.5)) && ((static_cast<uint32_t>(whole) & 1u) != 0))
{
// exactly 0.5 and ODD, then round up
// 1.5 -> 2, but 2.5 -> 2
++whole;
}
} else
{
unsigned int count = prec;
// now do fractional part, as an unsigned number
while (len < PRINTF_FTOA_BUFFER_SIZE)
{
--count;
buf[len++] = static_cast<char>(48U + (frac % 10U));
if ((frac /= 10U) == 0u)
{
break;
}
}
// add extra 0s
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U))
{
buf[len++] = '0';
}
if (len < PRINTF_FTOA_BUFFER_SIZE)
{
// add decimal
buf[len++] = '.';
}
}
// do whole part, number is reversed
while (len < PRINTF_FTOA_BUFFER_SIZE)
{
buf[len++] = static_cast<char>(48 + (whole % 10));
if ((whole /= 10) == 0)
{
break;
}
}
// pad leading zeros
if (((flags & FLAGS_LEFT) == 0u) && ((flags & FLAGS_ZEROPAD) != 0u))
{
if ((width != 0u) && (negative || ((flags & (FLAGS_PLUS | FLAGS_SPACE)) != 0u)))
{
width--;
}
while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE))
{
buf[len++] = '0';
}
}
if (len < PRINTF_FTOA_BUFFER_SIZE)
{
if (negative)
{
buf[len++] = '-';
} else if ((flags & FLAGS_PLUS) != 0u)
{
buf[len++] = '+'; // ignore the space if the '+' exists
} else if ((flags & FLAGS_SPACE) != 0u)
{
buf[len++] = ' ';
}
}
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
}
// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
static size_t _etoa(out_fct_type out, Vector<char>* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width,
unsigned int flags)
{
// check for NaN and special values
if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX))
{
return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
}
// determine the sign
const bool negative = value < 0;
if (negative)
{
value = -value;
}
// default precision
if ((flags & FLAGS_PRECISION) == 0u)
{
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// determine the decimal exponent
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
union
{
uint64_t U;
double F;
} conv {};
conv.F = value;
int exp2 = static_cast<int>((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
int expval = static_cast<int>(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
// now we want to compute 10^expval but we want to be sure it won't overflow
// exp2 = static_cast<int>(expval * 3.321928094887362 + 0.5);
exp2 = lround(expval * 3.321928094887362);
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
const double z2 = z * z;
conv.U = static_cast<uint64_t>(exp2 + 1023) << 52U;
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
// correct for rounding errors
if (value < conv.F)
{
expval--;
conv.F /= 10;
}
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
// in "%g" mode, "prec" is the number of *significant figures* not decimals
if ((flags & FLAGS_ADAPT_EXP) != 0u)
{
// do we want to fall-back to "%f" mode?
if ((value >= 1e-4) && (value < 1e6))
{
if (static_cast<int>(prec) > expval)
{
prec = static_cast<unsigned>(static_cast<int>(prec) - expval - 1);
} else
{
prec = 0;
}
flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
// no characters in exponent
minwidth = 0U;
expval = 0;
} else
{
// we use one sigfig for the whole part
if ((prec > 0) && ((flags & FLAGS_PRECISION) != 0u))
{
--prec;
}
}
}
// will everything fit?
unsigned int fwidth = width;
if (width > minwidth)
{
// we didn't fall-back so subtract the characters required for the exponent
fwidth -= minwidth;
} else
{
// not enough characters, so go back to default sizing
fwidth = 0U;
}
if (((flags & FLAGS_LEFT) != 0u) && (minwidth != 0u))
{
// if we're padding on the right, DON'T pad the floating part
fwidth = 0U;
}
// rescale the float value
if (expval != 0)
{
value /= conv.F;
}
// output the floating part
const size_t start_idx = idx;
idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
// output the exponent part
if (minwidth != 0u)
{
// output the exponential symbol
out((flags & FLAGS_UPPERCASE) != 0u ? 'E' : 'e', buffer, idx++, maxlen);
// output the exponent value
idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1,
FLAGS_ZEROPAD | FLAGS_PLUS);
// might need to right-pad spaces
if ((flags & FLAGS_LEFT) != 0u)
{
while (idx - start_idx < width)
{
out(' ', buffer, idx++, maxlen);
}
}
}
return idx;
}
static inline unsigned int _strnlen_s(const char* str, size_t maxsize)
{
const char* s = nullptr;
for (s = str; (*s != 0) && ((maxsize--) != 0u); ++s)
{
;
}
return static_cast<unsigned int>(s - str);
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
int my_vprint(const char* format, VaList* va_list)
{
Vector<char> buffer;
uint32_t flags = 0;
uint32_t width = 0;
uint32_t precision = 0;
uint32_t n = 0;
size_t idx = 0U;
auto maxlen = static_cast<size_t>(-1);
// use null output function
auto out = _out_null;
while (*format != 0)
{
// format specifier? %[flags][width][.precision][length]
if (*format != '%')
{
// no
out(*format, &buffer, idx++, maxlen);
format++;
continue;
}
// yes, evaluate it
format++;
// evaluate flags
flags = 0U;
do
{
switch (*format)
{
case '0':
flags |= FLAGS_ZEROPAD;
format++;
n = 1U;
break;
case '-':
flags |= FLAGS_LEFT;
format++;
n = 1U;
break;
case '+':
flags |= FLAGS_PLUS;
format++;
n = 1U;
break;
case ' ':
flags |= FLAGS_SPACE;
format++;
n = 1U;
break;
case '#':
flags |= FLAGS_HASH;
format++;
n = 1U;
break;
default: n = 0U; break;
}
} while (n != 0u);
// evaluate width field
width = 0U;
if (_is_digit(*format))
{
width = _atoi(&format);
} else if (*format == '*')
{
// const int w = va_arg(va, int);
const int w = VaArg_int(va_list);
if (w < 0)
{
flags |= FLAGS_LEFT; // reverse padding
width = static_cast<unsigned int>(-w);
} else
{
width = static_cast<unsigned int>(w);
}
format++;
}
// evaluate precision field
precision = 0U;
if (*format == '.')
{
flags |= FLAGS_PRECISION;
format++;
if (_is_digit(*format))
{
precision = _atoi(&format);
} else if (*format == '*')
{
// const int prec = (int)va_arg(va, int);
const int prec = VaArg_int(va_list);
precision = prec > 0 ? static_cast<unsigned int>(prec) : 0U;
format++;
}
}
// evaluate length field
switch (*format)
{
case 'l':
flags |= FLAGS_LONG;
format++;
if (*format == 'l')
{
flags |= FLAGS_LONG_LONG;
format++;
}
break;
case 'h':
flags |= FLAGS_SHORT;
format++;
if (*format == 'h')
{
flags |= FLAGS_CHAR;
format++;
}
break;
case 't':
flags |= (sizeof(ptrdiff_t) == sizeof(int32_t) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
case 'j':
flags |= (sizeof(intmax_t) == sizeof(int32_t) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
case 'z':
flags |= (sizeof(size_t) == sizeof(int32_t) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
default: break;
}
// evaluate specifier
switch (*format)
{
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'o':
case 'b':
{
// set the base
unsigned int base = 0;
if (*format == 'x' || *format == 'X')
{
base = 16U;
} else if (*format == 'o')
{
base = 8U;
} else if (*format == 'b')
{
base = 2U;
} else
{
base = 10U;
flags &= ~FLAGS_HASH; // no hash for dec format
}
// uppercase
if (*format == 'X')
{
flags |= FLAGS_UPPERCASE;
}
// no plus or space flag for u, x, X, o, b
if ((*format != 'i') && (*format != 'd'))
{
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
}
// ignore '0' flag when precision is given
if ((flags & FLAGS_PRECISION) != 0u)
{
flags &= ~FLAGS_ZEROPAD;
}
// convert the integer
if ((*format == 'i') || (*format == 'd'))
{
// signed
if ((flags & FLAGS_LONG_LONG) != 0u || (flags & FLAGS_LONG) != 0u)
{
// const long long value = va_arg(va, long long);
auto value = VaArg_long_long(va_list);
idx = _ntoa_long_long(out, &buffer, idx, maxlen, static_cast<uint64_t>(value > 0 ? value : 0 - value), value < 0,
base, precision, width, flags);
} else if ((flags & FLAGS_LONG) != 0u)
{
// const long value = va_arg(va, long);
auto value = VaArg_long(va_list);
idx = _ntoa_long(out, &buffer, idx, maxlen, static_cast<uint32_t>(value > 0 ? value : 0 - value), value < 0, base,
precision, width, flags);
} else
{
// const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int)
// : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int)
// : va_arg(va, int);
int value = (flags & FLAGS_CHAR) != 0u ? static_cast<char>(VaArg_int(va_list))
: (flags & FLAGS_SHORT) != 0u ? static_cast<int16_t>(VaArg_int(va_list))
: VaArg_int(va_list);
idx = _ntoa_long(out, &buffer, idx, maxlen, static_cast<unsigned int>(value > 0 ? value : 0 - value), value < 0,
base, precision, width, flags);
}
} else
{
// unsigned
if ((flags & FLAGS_LONG_LONG) != 0u || (flags & FLAGS_LONG) != 0u)
{
idx = _ntoa_long_long(out, &buffer, idx, maxlen, static_cast<uint64_t>(VaArg_long_long(va_list)), false, base,
precision, width, flags);
} else if ((flags & FLAGS_LONG) != 0u)
{
idx = _ntoa_long(out, &buffer, idx, maxlen, static_cast<uint32_t>(VaArg_long(va_list)), false, base, precision,
width, flags);
} else
{
const unsigned int value = (flags & FLAGS_CHAR) != 0u ? static_cast<unsigned char>(VaArg_int(va_list))
: (flags & FLAGS_SHORT) != 0u ? static_cast<uint16_t>(VaArg_int(va_list))
: static_cast<unsigned int>(VaArg_int(va_list));
idx = _ntoa_long(out, &buffer, idx, maxlen, value, false, base, precision, width, flags);
}
}
format++;
break;
}
case 'f':
case 'F':
if (*format == 'F')
{
flags |= FLAGS_UPPERCASE;
}
idx = _ftoa(out, &buffer, idx, maxlen, VaArg_double(va_list), precision, width, flags);
format++;
break;
case 'e':
case 'E':
case 'g':
case 'G':
if ((*format == 'g') || (*format == 'G'))
{
flags |= FLAGS_ADAPT_EXP;
}
if ((*format == 'E') || (*format == 'G'))
{
flags |= FLAGS_UPPERCASE;
}
idx = _etoa(out, &buffer, idx, maxlen, VaArg_double(va_list), precision, width, flags);
format++;
break;
case 'c':
{
unsigned int l = 1U;
// pre padding
if ((flags & FLAGS_LEFT) == 0u)
{
while (l++ < width)
{
out(' ', &buffer, idx++, maxlen);
}
}
// char output
out(static_cast<char>(VaArg_int(va_list)), &buffer, idx++, maxlen);
// post padding
if ((flags & FLAGS_LEFT) != 0u)
{
while (l++ < width)
{
out(' ', &buffer, idx++, maxlen);
}
}
format++;
break;
}
case 's':
{
// const char* p = va_arg(va, char*);
const char* p = VaArg_ptr<const char>(va_list);
unsigned int l = _strnlen_s(p, precision != 0u ? precision : static_cast<size_t>(-1));
// pre padding
if ((flags & FLAGS_PRECISION) != 0u)
{
l = (l < precision ? l : precision);
}
if ((flags & FLAGS_LEFT) == 0u)
{
while (l++ < width)
{
out(' ', &buffer, idx++, maxlen);
}
}
// string output
while ((*p != 0) && (((flags & FLAGS_PRECISION) == 0u) || ((precision--) != 0u)))
{
out(*(p++), &buffer, idx++, maxlen);
}
// post padding
if ((flags & FLAGS_LEFT) != 0u)
{
while (l++ < width)
{
out(' ', &buffer, idx++, maxlen);
}
}
format++;
break;
}
case 'p':
{
width = sizeof(void*) * 2U;
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
const bool is_ll = sizeof(uintptr_t) == sizeof(int64_t);
if (is_ll)
{
idx = _ntoa_long_long(out, &buffer, idx, maxlen, reinterpret_cast<uintptr_t>(VaArg_ptr<void>(va_list)), false, 16U,
precision, width, flags);
} else
{
idx =
_ntoa_long(out, &buffer, idx, maxlen, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(VaArg_ptr<void>(va_list))),
false, 16U, precision, width, flags);
}
format++;
break;
}
case '%':
out('%', &buffer, idx++, maxlen);
format++;
break;
default:
out(*format, &buffer, idx++, maxlen);
format++;
break;
}
}
// termination
out(static_cast<char>(0), &buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
printf(FG_BRIGHT_MAGENTA "%s" DEFAULT, buffer.GetDataConst());
// return written chars without terminating \0
return static_cast<int>(idx);
}
int my_print_v(VaContext* ctx)
{
const char* format = VaArg_ptr<const char>(&ctx->va_list);
return my_vprint(format, &ctx->va_list);
}
int KYTY_SYSV_ABI my_print2(VA_ARGS)
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
VA_CONTEXT(ctx);
return my_print_v(&ctx);
}
libc_print_func_t GetPrintFunc()
{
return reinterpret_cast<libc_print_func_t>(my_print2);
}
libc_print_v_func_t GetPrintFuncV()
{
return my_print_v;
}
libc_vprint_func_t GetVPrintFunc()
{
return my_vprint;
}
} // namespace Kyty::Libs
#endif // KYTY_EMU_ENABLED
+207
View File
@@ -0,0 +1,207 @@
#include "Emulator/Log.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/File.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Subsystems.h"
#include "Kyty/Core/Threads.h"
#include "Emulator/Common.h"
#include "Emulator/Config.h"
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
#include <windows.h>
// IWYU pragma: no_include <handleapi.h>
// IWYU pragma: no_include <minwindef.h>
// IWYU pragma: no_include <processenv.h>
#endif
#ifdef KYTY_EMU_ENABLED
namespace Kyty {
namespace Log {
static bool g_log_initialized = false;
static Core::Mutex* g_mutex = nullptr;
static Direction g_dir = Direction::Console;
static Core::File* g_file = nullptr;
static bool g_colored_printf = false;
static bool EnableVTMode()
{
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
// Set output mode to handle virtual terminal sequences
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
if (h == INVALID_HANDLE_VALUE)
{
return false;
}
DWORD dw_mode = 0;
if (GetConsoleMode(h, &dw_mode) == 0)
{
return false;
}
dw_mode |= static_cast<DWORD>(ENABLE_VIRTUAL_TERMINAL_PROCESSING);
return (SetConsoleMode(h, dw_mode) != 0);
#endif
return true;
}
bool IsColoredPrintf()
{
return g_colored_printf;
}
String RemoveColors(const String& str)
{
uint32_t start = 0;
String ret;
for (;;)
{
auto index = str.FindIndex(U'\x1b', start);
if (!str.IndexValid(index))
{
ret += str.Mid(start);
break;
}
ret += str.Mid(start, index - start);
index = str.FindIndex(U'm', index);
if (!str.IndexValid(index))
{
break;
}
start = index + 1;
}
return ret;
}
static void Close()
{
if (g_log_initialized)
{
g_mutex->Lock();
if (g_dir == Direction::File && g_file != nullptr)
{
g_file->Flush();
g_file->Close();
delete g_file;
g_file = nullptr;
}
g_mutex->Unlock();
}
}
KYTY_SUBSYSTEM_INIT(Log)
{
if (!g_log_initialized)
{
g_mutex = new Core::Mutex;
g_log_initialized = true;
}
auto dir = Config::GetPrintfDirection();
SetDirection(dir);
if (dir == Log::Direction::File)
{
SetOutputFile(Config::GetPrintfOutputFile());
}
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Log)
{
Close();
}
KYTY_SUBSYSTEM_DESTROY(Log)
{
Close();
}
void SetDirection(Direction dir)
{
EXIT_IF(!Log::g_log_initialized);
EXIT_IF(!Core::Thread::IsMainThread());
if (dir == Direction::Console)
{
g_colored_printf = EnableVTMode();
if (!g_colored_printf)
{
::printf("Colored printf is not supported\n");
}
} else
{
g_colored_printf = false;
}
g_dir = dir;
}
void SetOutputFile(const String& file_name, Core::File::Encoding enc)
{
EXIT_IF(!Log::g_log_initialized);
EXIT_IF(!Core::Thread::IsMainThread());
EXIT_IF(Log::g_dir != Log::Direction::File);
EXIT_IF(Log::g_file != nullptr);
g_file = new Core::File;
g_file->Create(file_name);
if (g_file->IsInvalid())
{
::printf("Can't create log file: %s\n", file_name.C_Str());
delete g_file;
g_file = nullptr;
} else
{
g_file->SetEncoding(enc);
g_file->WriteBOM();
}
}
} // namespace Log
void printf(const char* format, ...)
{
EXIT_IF(!Log::g_log_initialized);
if (Log::g_dir == Log::Direction::Silent)
{
return;
}
EXIT_IF(Log::g_mutex == nullptr);
Log::g_mutex->Lock();
{
va_list args {};
va_start(args, format);
String s;
s.Printf(format, args);
va_end(args);
if (!Log::g_colored_printf)
{
s = Log::RemoveColors(s);
}
if (Log::g_dir == Log::Direction::Console)
{
::printf("%s", s.C_Str());
} else if (Log::g_dir == Log::Direction::File && Log::g_file != nullptr)
{
Log::g_file->Write(s);
}
}
Log::g_mutex->Unlock();
}
} // namespace Kyty
#endif // KYTY_EMU_ENABLED
+50
View File
@@ -0,0 +1,50 @@
#include "Emulator/Profiler.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Subsystems.h"
#include "Emulator/Config.h"
#include <easy/profiler.h>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Profiler {
void Close()
{
auto dir = Config::GetProfilerDirection();
if (dir == Config::ProfilerDirection::File || dir == Config::ProfilerDirection::FileAndNetwork)
{
profiler::dumpBlocksToFile(Config::GetProfilerOutputFile().C_Str());
}
}
KYTY_SUBSYSTEM_INIT(Profiler)
{
switch (Config::GetProfilerDirection())
{
case Config::ProfilerDirection::File: EASY_PROFILER_ENABLE; break;
case Config::ProfilerDirection::Network: profiler::startListen(); break;
case Config::ProfilerDirection::FileAndNetwork:
EASY_PROFILER_ENABLE;
profiler::startListen();
break;
case Config::ProfilerDirection::None:
default: break;
}
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Profiler)
{
Close();
}
KYTY_SUBSYSTEM_DESTROY(Profiler)
{
Close();
}
} // namespace Kyty::Profiler
#endif // KYTY_EMU_ENABLED
File diff suppressed because it is too large Load Diff
+77
View File
@@ -0,0 +1,77 @@
#include "Emulator/SymbolDatabase.h"
#include "Kyty/Core/File.h"
#include "Kyty/Core/MagicEnum.h"
#include "Kyty/Core/Vector.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Loader {
constexpr char32_t LIB_PREFIX[] = {0x0000006c, 0x00000069, 0x00000062, 0x00000053, 0x00000063, 0x00000065, 0};
constexpr char32_t LIB_OLD[] = {0x00000047, 0x0000006e, 0x0000006d, 0};
constexpr char32_t LIB_NEW[] = {0x00000047, 0x00000072, 0x00000061, 0x00000070, 0x00000068, 0x00000069, 0x00000063, 0x00000073, 0};
static String update_name(const String& str)
{
auto ret = (str.StartsWith(LIB_PREFIX) ? str.RemoveFirst(6) : str);
return ret.ReplaceStr(LIB_OLD, LIB_NEW);
}
String SymbolDatabase::GenerateName(const SymbolResolve& s)
{
auto library = update_name(s.library);
auto module = update_name(s.module);
return String::FromPrintf("%s[%s_v%d][%s_v%d.%d][%s]", s.name.C_Str(), library.C_Str(), s.library_version, module.C_Str(),
s.module_version_major, s.module_version_minor, Core::EnumName(s.type).C_Str());
}
void SymbolDatabase::Add(const SymbolResolve& s, uint64_t vaddr)
{
SymbolRecord r {};
r.name = GenerateName(s);
r.vaddr = vaddr;
m_map.Put(r.name, m_symbols.Size());
m_symbols.Add(r);
}
void SymbolDatabase::Add(const SymbolResolve& s, uint64_t vaddr, const String& dbg_name)
{
SymbolRecord r {};
r.name = GenerateName(s);
r.vaddr = vaddr;
r.dbg_name = dbg_name;
m_map.Put(r.name, m_symbols.Size());
m_symbols.Add(r);
}
void SymbolDatabase::DbgDump(const String& folder, const String& file_name)
{
auto folder_str = folder.FixDirectorySlash();
Core::File::CreateDirectories(folder_str);
Core::File f;
f.Create(folder_str + file_name);
for (const auto& sym: m_symbols)
{
f.Printf("%" PRIx64 " %s\n", sym.vaddr, sym.name.C_Str());
}
f.Close();
}
const SymbolRecord* SymbolDatabase::Find(const SymbolResolve& s) const
{
auto index = m_map.Get(GenerateName(s), uint32_t(-1));
if (!m_symbols.IndexValid(index))
{
return nullptr;
}
return &m_symbols.At(index);
}
} // namespace Kyty::Loader
#endif // KYTY_EMU_ENABLED
+51
View File
@@ -0,0 +1,51 @@
#include "Kyty/Core/Timer.h"
#include "Kyty/Core/DateTime.h"
#include "Kyty/Core/Subsystems.h"
#include "Emulator/Common.h"
#include "Emulator/Timer.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Loader::Timer {
static Core::Timer g_timer;
KYTY_SUBSYSTEM_INIT(Timer)
{
Start();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Timer) {}
KYTY_SUBSYSTEM_DESTROY(Timer) {}
void Start()
{
g_timer.Start();
}
double GetTimeMs()
{
return g_timer.GetTimeMs();
}
Core::Time GetTime()
{
return Core::Time(static_cast<int>(GetTimeMs()));
}
uint64_t GetCounter()
{
return g_timer.GetTicks();
}
uint64_t GetFrequency()
{
return g_timer.GetFrequency();
}
} // namespace Kyty::Loader::Timer
#endif // KYTY_EMU_ENABLED
+341
View File
@@ -0,0 +1,341 @@
#include "Emulator/VirtualMemory.h"
#include "Kyty/Core/DbgAssert.h"
#include "Emulator/Common.h"
#include "Emulator/Jit.h"
#include <new>
// NOLINTNEXTLINE
//#define NTDDI_VERSION 0x0A000005
#include <windows.h> // IWYU pragma: keep
// IWYU pragma: no_include <minwindef.h>
// IWYU pragma: no_include <sysinfoapi.h>
// IWYU pragma: no_include <memoryapi.h>
// IWYU pragma: no_include <errhandlingapi.h>
// IWYU pragma: no_include <processthreadsapi.h>
// IWYU pragma: no_include <basetsd.h>
// IWYU pragma: no_include <excpt.h>
// IWYU pragma: no_include <wtypes.h>
// IWYU pragma: no_include <minwinbase.h>
// IWYU pragma: no_include <apisetcconv.h>
//#include <memoryapi.h>
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Loader {
SystemInfo GetSystemInfo()
{
SystemInfo ret {};
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
switch (system_info.wProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_AMD64: ret.ProcessorArchitecture = ProcessorArchitecture::Amd64; break;
case PROCESSOR_ARCHITECTURE_UNKNOWN:
default: ret.ProcessorArchitecture = ProcessorArchitecture::Unknown;
}
ret.PageSize = system_info.dwPageSize;
ret.MinimumApplicationAddress = reinterpret_cast<uintptr_t>(system_info.lpMinimumApplicationAddress);
ret.MaximumApplicationAddress = reinterpret_cast<uintptr_t>(system_info.lpMaximumApplicationAddress);
ret.ActiveProcessorMask = system_info.dwActiveProcessorMask;
ret.NumberOfProcessors = system_info.dwNumberOfProcessors;
ret.ProcessorLevel = system_info.wProcessorLevel;
ret.ProcessorRevision = system_info.wProcessorRevision;
return ret;
}
namespace VirtualMemory {
class ExceptionHandlerPrivate
{
public:
#pragma pack(1)
struct UnwindInfo
{
uint8_t Version : 3;
uint8_t Flags : 5;
uint8_t SizeOfProlog;
uint8_t CountOfCodes;
uint8_t FrameRegister : 4;
uint8_t FrameOffset : 4;
ULONG ExceptionHandler;
ExceptionHandlerPrivate* ExceptionData;
};
struct HandlerInfo
{
Jit::JmpRax code;
RUNTIME_FUNCTION function_table = {};
UnwindInfo unwind_info = {};
};
#pragma pack()
static EXCEPTION_DISPOSITION Handler(PEXCEPTION_RECORD exception_record, ULONG64 /*EstablisherFrame*/, PCONTEXT /*ContextRecord*/,
PDISPATCHER_CONTEXT dispatcher_context)
{
ExceptionHandler::ExceptionInfo info {};
if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
info.type = ExceptionHandler::ExceptionType::AccessViolation;
switch (exception_record->ExceptionInformation[0])
{
case 0: info.access_violation_type = ExceptionHandler::AccessViolationType::Read; break;
case 1: info.access_violation_type = ExceptionHandler::AccessViolationType::Write; break;
case 8: info.access_violation_type = ExceptionHandler::AccessViolationType::Execute; break;
default: info.access_violation_type = ExceptionHandler::AccessViolationType::Unknown; break;
}
info.access_violation_vaddr = exception_record->ExceptionInformation[1];
}
auto* p = *static_cast<ExceptionHandlerPrivate**>(dispatcher_context->HandlerData);
p->func(&info);
return ExceptionContinueExecution;
}
void InitHandler()
{
auto* h = new (reinterpret_cast<void*>(handler_addr)) HandlerInfo;
auto* code = &h->code;
auto* unwind_info = &h->unwind_info;
function_table = &h->function_table;
function_table->BeginAddress = 0;
function_table->EndAddress = image_size;
function_table->UnwindData = reinterpret_cast<uintptr_t>(unwind_info) - base_address;
unwind_info->Version = 1;
unwind_info->Flags = UNW_FLAG_EHANDLER;
unwind_info->SizeOfProlog = 0;
unwind_info->CountOfCodes = 0;
unwind_info->FrameRegister = 0;
unwind_info->FrameOffset = 0;
unwind_info->ExceptionHandler = reinterpret_cast<uintptr_t>(code) - base_address;
unwind_info->ExceptionData = this;
code->SetFunc(Handler);
FlushInstructionCache(reinterpret_cast<uint64_t>(code), sizeof(h->code));
}
uint64_t base_address = 0;
uint64_t handler_addr = 0;
uint64_t image_size = 0;
PRUNTIME_FUNCTION function_table = nullptr;
ExceptionHandler::handler_func_t func = nullptr;
};
ExceptionHandler::ExceptionHandler(): m_p(new ExceptionHandlerPrivate) {}
ExceptionHandler::~ExceptionHandler()
{
Uninstall();
delete m_p;
}
uint64_t ExceptionHandler::GetSize()
{
return (sizeof(ExceptionHandlerPrivate::HandlerInfo) & ~(uint64_t(0x1000) - 1)) + 0x1000;
}
bool ExceptionHandler::Install(uint64_t base_address, uint64_t handler_addr, uint64_t image_size, handler_func_t func)
{
if (m_p->function_table == nullptr)
{
m_p->base_address = base_address;
m_p->handler_addr = handler_addr;
m_p->image_size = image_size;
m_p->func = func;
m_p->InitHandler();
if (RtlAddFunctionTable(m_p->function_table, 1, base_address) == FALSE)
{
printf("RtlAddFunctionTable() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false;
}
return true;
}
return false;
}
bool ExceptionHandler::Uninstall()
{
if (m_p->function_table != nullptr)
{
if (RtlDeleteFunctionTable(m_p->function_table) == FALSE)
{
printf("RtlDeleteFunctionTable() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false;
}
m_p->function_table = nullptr;
return true;
}
return false;
}
static DWORD get_protection_flag(VirtualMemory::Mode mode)
{
DWORD protect = PAGE_NOACCESS;
switch (mode)
{
case VirtualMemory::Mode::Read: protect = PAGE_READONLY; break;
case VirtualMemory::Mode::Write:
case VirtualMemory::Mode::ReadWrite: protect = PAGE_READWRITE; break;
case VirtualMemory::Mode::Execute: protect = PAGE_EXECUTE; break;
case VirtualMemory::Mode::ExecuteRead: protect = PAGE_EXECUTE_READ; break;
case VirtualMemory::Mode::ExecuteWrite:
case VirtualMemory::Mode::ExecuteReadWrite: protect = PAGE_EXECUTE_READWRITE; break;
case VirtualMemory::Mode::NoAccess:
default: protect = PAGE_NOACCESS; break;
}
return protect;
}
static VirtualMemory::Mode get_protection_flag(DWORD mode)
{
switch (mode)
{
case PAGE_NOACCESS: return VirtualMemory::Mode::NoAccess;
case PAGE_READONLY: return VirtualMemory::Mode::Read;
case PAGE_READWRITE: return VirtualMemory::Mode::ReadWrite;
case PAGE_EXECUTE: return VirtualMemory::Mode::Execute;
case PAGE_EXECUTE_READ: return VirtualMemory::Mode::ExecuteRead;
case PAGE_EXECUTE_READWRITE: return VirtualMemory::Mode::ExecuteReadWrite;
default: return VirtualMemory::Mode::NoAccess;
}
}
uint64_t Alloc(uint64_t address, uint64_t size, Mode mode)
{
auto ptr = reinterpret_cast<uintptr_t>(VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE),
get_protection_flag(mode)));
if (ptr == 0)
{
printf("VirtualAlloc() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
}
return ptr;
}
using VirtualAlloc2_func_t = /*WINBASEAPI*/ PVOID WINAPI (*)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG);
static VirtualAlloc2_func_t ResolveVirtualAlloc2()
{
HMODULE h = GetModuleHandle("KernelBase");
if (h != nullptr)
{
return reinterpret_cast<VirtualAlloc2_func_t>(GetProcAddress(h, "VirtualAlloc2"));
}
return nullptr;
}
uint64_t AllocAligned(uint64_t /*address*/, uint64_t size, Mode mode, uint64_t alignment)
{
MEM_ADDRESS_REQUIREMENTS req2 {};
MEM_EXTENDED_PARAMETER param {};
req2.LowestStartingAddress = nullptr;
req2.HighestEndingAddress = reinterpret_cast<PVOID>(0xffffffffffu); // nullptr;
req2.Alignment = alignment;
param.Type = MemExtendedParameterAddressRequirements;
param.Pointer = &req2;
static auto virtual_alloc2 = ResolveVirtualAlloc2();
EXIT_NOT_IMPLEMENTED(virtual_alloc2 == nullptr);
auto ptr = reinterpret_cast<uintptr_t>(virtual_alloc2(GetCurrentProcess(), nullptr, size,
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE),
get_protection_flag(mode), &param, 1));
if (ptr == 0)
{
printf("VirtualAlloc2() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
}
return ptr;
}
bool Free(uint64_t address)
{
if (VirtualFree(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), 0, MEM_RELEASE) == 0)
{
printf("VirtualFree() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false;
}
return true;
}
bool Protect(uint64_t address, uint64_t size, Mode mode, Mode* old_mode)
{
DWORD old_protect = 0;
if (VirtualProtect(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size, get_protection_flag(mode), &old_protect) == 0)
{
printf("VirtualProtect() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false;
}
if (old_mode != nullptr)
{
*old_mode = get_protection_flag(old_protect);
}
return true;
}
bool FlushInstructionCache(uint64_t address, uint64_t size)
{
if (::FlushInstructionCache(GetCurrentProcess(), reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size) == 0)
{
printf("FlushInstructionCache() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
return false;
}
return true;
}
bool PatchReplace(uint64_t vaddr, uint64_t value)
{
VirtualMemory::Mode old_mode {};
VirtualMemory::Protect(vaddr, 8, VirtualMemory::Mode::ReadWrite, &old_mode);
auto* ptr = reinterpret_cast<uint64_t*>(vaddr);
bool ret = (*ptr != value);
*ptr = value;
VirtualMemory::Protect(vaddr, 8, old_mode);
if (VirtualMemory::IsExecute(old_mode))
{
VirtualMemory::FlushInstructionCache(vaddr, 8);
}
return ret;
}
} // namespace VirtualMemory
} // namespace Kyty::Loader
#endif // KYTY_EMU_ENABLED