mirror of
https://github.com/InoriRus/Kyty.git
synced 2026-08-28 05:06:40 +00:00
Initial commit
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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, ®ion);
|
||||
|
||||
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, ®ion, 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, ©_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
|
||||
@@ -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
@@ -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, µs);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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), ¶m, 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
|
||||
Reference in New Issue
Block a user