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,48 @@
|
||||
file(GLOB emulator_src
|
||||
src/*.cpp
|
||||
src/*.cpp
|
||||
src/Libs/*.cpp
|
||||
src/Graphics/*.cpp
|
||||
src/Kernel/*.cpp
|
||||
include/*.h
|
||||
)
|
||||
|
||||
if (MSVC AND CLANG)
|
||||
set_source_files_properties(${emulator_src} PROPERTIES COMPILE_FLAGS "-Wno-pragma-pack -Wno-deprecated-declarations -D_TIMESPEC_DEFINED")
|
||||
endif()
|
||||
|
||||
add_library(emulator_obj OBJECT ${emulator_src})
|
||||
add_library(emulator STATIC $<TARGET_OBJECTS:emulator_obj>)
|
||||
|
||||
target_link_libraries(emulator core math scripts lua vulkan-1 spirv-tools spirv-tools-opt easy_profiler)
|
||||
|
||||
target_include_directories(emulator PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
get_property(inc_headers TARGET emulator PROPERTY INCLUDE_DIRECTORIES)
|
||||
|
||||
list(APPEND inc_headers
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/sdl2/include
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/vulkan/include
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/easy_profiler/include
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/xxhash/include
|
||||
)
|
||||
|
||||
if (MSVC AND CLANG)
|
||||
target_link_libraries(emulator winpthread)
|
||||
list(APPEND inc_headers
|
||||
${CMAKE_SOURCE_DIR}/3rdparty/winpthread/include
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(emulator_obj PRIVATE ${inc_headers})
|
||||
|
||||
list(APPEND check_headers
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
#${CMAKE_SOURCE_DIR}/3rdparty/affinity/include
|
||||
)
|
||||
|
||||
clang_tidy_check(emulator_obj "" "${check_headers}" "${inc_headers}")
|
||||
|
||||
include_what_you_use(emulator_obj "${inc_headers}")
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_COMMON_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_COMMON_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#if (KYTY_COMPILER == KYTY_COMPILER_CLANG || KYTY_COMPILER == KYTY_COMPILER_MINGW) && KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BITNESS == 64 && \
|
||||
KYTY_ENDIAN == KYTY_ENDIAN_LITTLE && KYTY_ABI == KYTY_ABI_X86_64 && KYTY_PROJECT == KYTY_PROJECT_EMULATOR
|
||||
#define KYTY_EMU_ENABLED
|
||||
#endif
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
#include "Emulator/Log.h" // IWYU pragma: export
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define KYTY_MS_ABI __attribute__((ms_abi))
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define KYTY_SYSV_ABI __attribute__((sysv_abi))
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_COMMON_H_ */
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_CONFIG_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_CONFIG_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Scripts {
|
||||
class ScriptVar;
|
||||
} // namespace Kyty::Scripts
|
||||
|
||||
namespace Kyty::Config {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Config);
|
||||
|
||||
enum class ShaderOptimizationType
|
||||
{
|
||||
None,
|
||||
Size,
|
||||
Performance
|
||||
};
|
||||
|
||||
enum class ShaderLogDirection
|
||||
{
|
||||
Silent,
|
||||
Console,
|
||||
File
|
||||
};
|
||||
|
||||
enum class ProfilerDirection
|
||||
{
|
||||
None,
|
||||
File,
|
||||
Network,
|
||||
FileAndNetwork
|
||||
};
|
||||
|
||||
void Load(const Scripts::ScriptVar& cfg);
|
||||
|
||||
uint32_t GetScreenWidth();
|
||||
uint32_t GetScreenHeight();
|
||||
bool IsNeo();
|
||||
bool VulkanValidationEnabled();
|
||||
|
||||
bool ShaderValidationEnabled();
|
||||
ShaderOptimizationType GetShaderOptimizationType();
|
||||
ShaderLogDirection GetShaderLogDirection();
|
||||
String GetShaderLogFolder();
|
||||
|
||||
bool CommandBufferDumpEnabled();
|
||||
String GetCommandBufferDumpFolder();
|
||||
|
||||
Log::Direction GetPrintfDirection();
|
||||
String GetPrintfOutputFile();
|
||||
|
||||
ProfilerDirection GetProfilerDirection();
|
||||
String GetProfilerOutputFile();
|
||||
|
||||
} // namespace Kyty::Config
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_CONFIG_H_ */
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_CONTROLLER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_CONTROLLER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Controller {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Controller);
|
||||
|
||||
constexpr uint32_t PAD_BUTTON_L3 = 0x00000002;
|
||||
constexpr uint32_t PAD_BUTTON_R3 = 0x00000004;
|
||||
constexpr uint32_t PAD_BUTTON_OPTIONS = 0x00000008;
|
||||
constexpr uint32_t PAD_BUTTON_UP = 0x00000010;
|
||||
constexpr uint32_t PAD_BUTTON_RIGHT = 0x00000020;
|
||||
constexpr uint32_t PAD_BUTTON_DOWN = 0x00000040;
|
||||
constexpr uint32_t PAD_BUTTON_LEFT = 0x00000080;
|
||||
constexpr uint32_t PAD_BUTTON_L2 = 0x00000100;
|
||||
constexpr uint32_t PAD_BUTTON_R2 = 0x00000200;
|
||||
constexpr uint32_t PAD_BUTTON_L1 = 0x00000400;
|
||||
constexpr uint32_t PAD_BUTTON_R1 = 0x00000800;
|
||||
constexpr uint32_t PAD_BUTTON_TRIANGLE = 0x00001000;
|
||||
constexpr uint32_t PAD_BUTTON_CIRCLE = 0x00002000;
|
||||
constexpr uint32_t PAD_BUTTON_CROSS = 0x00004000;
|
||||
constexpr uint32_t PAD_BUTTON_SQUARE = 0x00008000;
|
||||
constexpr uint32_t PAD_BUTTON_TOUCH_PAD = 0x00100000;
|
||||
|
||||
enum class Axis
|
||||
{
|
||||
LeftX = 0,
|
||||
LeftY = 1,
|
||||
RightX = 2,
|
||||
RightY = 3,
|
||||
TriggerLeft = 4,
|
||||
TriggerRight = 5,
|
||||
|
||||
AxisMax
|
||||
};
|
||||
|
||||
struct PadControllerInformation;
|
||||
struct PadData;
|
||||
|
||||
inline int controller_get_axis(int min, int max, int value)
|
||||
{
|
||||
int v = (255 * (value - min)) / (max - min);
|
||||
return (v < 0 ? 0 : (v > 255 ? 255 : v));
|
||||
}
|
||||
|
||||
void ControllerConnect(int id);
|
||||
void ControllerDisconnect(int id);
|
||||
void ControllerButton(int id, uint32_t button, bool down);
|
||||
void ControllerAxis(int id, Axis axis, int value);
|
||||
|
||||
int KYTY_SYSV_ABI PadInit();
|
||||
int KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param);
|
||||
int KYTY_SYSV_ABI PadSetMotionSensorState(int handle, bool enable);
|
||||
int KYTY_SYSV_ABI PadGetControllerInformation(int handle, PadControllerInformation* info);
|
||||
int KYTY_SYSV_ABI PadReadState(int handle, PadData* data);
|
||||
|
||||
} // namespace Kyty::Libs::Controller
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_CONTROLLER_H_ */
|
||||
@@ -0,0 +1,285 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_ELF_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_ELF_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Vector.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
namespace Kyty::Core {
|
||||
class File;
|
||||
} // namespace Kyty::Core
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Loader {
|
||||
|
||||
using Elf64_Addr = uint64_t; // Unsigned program address
|
||||
using Elf64_Off = uint64_t; // Unsigned file offset
|
||||
using Elf64_Half = uint16_t; // Unsigned medium integer
|
||||
using Elf64_Word = uint32_t; // Unsigned integer
|
||||
using Elf64_Sword = int32_t; // Signed integer
|
||||
using Elf64_Xword = uint64_t; // Unsigned long integer
|
||||
using Elf64_Sxword = int64_t; // Signed long integer
|
||||
|
||||
constexpr int EI_MAG0 = 0;
|
||||
constexpr int EI_MAG1 = 1;
|
||||
constexpr int EI_MAG2 = 2;
|
||||
constexpr int EI_MAG3 = 3;
|
||||
constexpr int EI_CLASS = 4;
|
||||
constexpr int EI_DATA = 5;
|
||||
constexpr int EI_VERSION = 6;
|
||||
constexpr int EI_OSABI = 7;
|
||||
constexpr int EI_ABIVERSION = 8;
|
||||
constexpr int EI_PAD = 9;
|
||||
constexpr int EI_NIDENT = 16;
|
||||
|
||||
constexpr char ELFCLASS64 = 2; // 64-bit objects
|
||||
|
||||
constexpr char ELFDATA2LSB = 1; // Object file data structures are little-endian
|
||||
|
||||
constexpr Elf64_Half EM_X86_64 = 62; /* AMD x86-64 architecture */
|
||||
|
||||
constexpr int EV_CURRENT = 1;
|
||||
|
||||
constexpr char ELFOSABI_FREEBSD = 9; // FreeBSD operating system
|
||||
|
||||
constexpr Elf64_Half ET_DYNEXEC = 0xfe10; // Executable file
|
||||
constexpr Elf64_Half ET_DYNAMIC = 0xfe18; // Shared
|
||||
|
||||
//#define SHT_PROGBITS 1
|
||||
|
||||
constexpr Elf64_Word PT_LOAD = 1;
|
||||
constexpr Elf64_Word PT_DYNAMIC = 2;
|
||||
constexpr Elf64_Word PT_TLS = 7;
|
||||
constexpr Elf64_Word PT_OS_DYNLIBDATA = 0x61000000;
|
||||
constexpr Elf64_Word PT_OS_PROCPARAM = 0x61000001;
|
||||
constexpr Elf64_Word PT_OS_RELRO = 0x61000010;
|
||||
|
||||
constexpr Elf64_Word PF_X = 0x1; // Execute
|
||||
constexpr Elf64_Word PF_W = 0x2; // Write
|
||||
constexpr Elf64_Word PF_R = 0x4; // Read
|
||||
|
||||
constexpr Elf64_Sxword DT_DEBUG = 0x00000015;
|
||||
constexpr Elf64_Sxword DT_FINI = 0x0000000d;
|
||||
constexpr Elf64_Sxword DT_FINI_ARRAY = 0x0000001a;
|
||||
constexpr Elf64_Sxword DT_FINI_ARRAYSZ = 0x0000001c;
|
||||
constexpr Elf64_Sxword DT_FLAGS = 0x0000001e;
|
||||
constexpr Elf64_Sxword DT_INIT = 0x0000000c;
|
||||
constexpr Elf64_Sxword DT_INIT_ARRAY = 0x00000019;
|
||||
constexpr Elf64_Sxword DT_INIT_ARRAYSZ = 0x0000001b;
|
||||
constexpr Elf64_Sxword DT_NEEDED = 0x00000001;
|
||||
constexpr Elf64_Sxword DT_OS_EXPORT_LIB = 0x61000013;
|
||||
constexpr Elf64_Sxword DT_OS_EXPORT_LIB_1 = 0x61000047;
|
||||
constexpr Elf64_Sxword DT_OS_EXPORT_LIB_ATTR = 0x61000017;
|
||||
constexpr Elf64_Sxword DT_OS_FINGERPRINT = 0x61000007;
|
||||
constexpr Elf64_Sxword DT_OS_HASH = 0x61000025;
|
||||
constexpr Elf64_Sxword DT_OS_HASHSZ = 0x6100003d;
|
||||
constexpr Elf64_Sxword DT_OS_IMPORT_LIB = 0x61000015;
|
||||
constexpr Elf64_Sxword DT_OS_IMPORT_LIB_1 = 0x61000049;
|
||||
constexpr Elf64_Sxword DT_OS_IMPORT_LIB_ATTR = 0x61000019;
|
||||
constexpr Elf64_Sxword DT_OS_JMPREL = 0x61000029;
|
||||
constexpr Elf64_Sxword DT_OS_MODULE_ATTR = 0x61000011;
|
||||
constexpr Elf64_Sxword DT_OS_MODULE_INFO = 0x6100000d;
|
||||
constexpr Elf64_Sxword DT_OS_MODULE_INFO_1 = 0x61000043;
|
||||
constexpr Elf64_Sxword DT_OS_NEEDED_MODULE = 0x6100000f;
|
||||
constexpr Elf64_Sxword DT_OS_NEEDED_MODULE_1 = 0x61000045;
|
||||
constexpr Elf64_Sxword DT_OS_ORIGINAL_FILENAME = 0x61000009;
|
||||
constexpr Elf64_Sxword DT_OS_ORIGINAL_FILENAME_1 = 0x61000041;
|
||||
constexpr Elf64_Sxword DT_OS_PLTGOT = 0x61000027;
|
||||
constexpr Elf64_Sxword DT_OS_PLTREL = 0x6100002b;
|
||||
constexpr Elf64_Sxword DT_OS_PLTRELSZ = 0x6100002d;
|
||||
constexpr Elf64_Sxword DT_OS_RELA = 0x6100002f;
|
||||
constexpr Elf64_Sxword DT_OS_RELAENT = 0x61000033;
|
||||
constexpr Elf64_Sxword DT_OS_RELASZ = 0x61000031;
|
||||
constexpr Elf64_Sxword DT_OS_STRSZ = 0x61000037;
|
||||
constexpr Elf64_Sxword DT_OS_STRTAB = 0x61000035;
|
||||
constexpr Elf64_Sxword DT_OS_SYMENT = 0x6100003b;
|
||||
constexpr Elf64_Sxword DT_OS_SYMTAB = 0x61000039;
|
||||
constexpr Elf64_Sxword DT_OS_SYMTABSZ = 0x6100003f;
|
||||
constexpr Elf64_Sxword DT_PREINIT_ARRAY = 0x00000020;
|
||||
constexpr Elf64_Sxword DT_PREINIT_ARRAYSZ = 0x00000021;
|
||||
constexpr Elf64_Sxword DT_REL = 0x00000011;
|
||||
constexpr Elf64_Sxword DT_RELA = 0x00000007;
|
||||
constexpr Elf64_Sxword DT_SONAME = 0x0000000e;
|
||||
constexpr Elf64_Sxword DT_TEXTREL = 0x00000016;
|
||||
|
||||
constexpr Elf64_Sxword DT_HASH = 0x00000004;
|
||||
constexpr Elf64_Sxword DT_STRTAB = 0x00000005;
|
||||
constexpr Elf64_Sxword DT_STRSZ = 0x0000000a;
|
||||
constexpr Elf64_Sxword DT_SYMTAB = 0x00000006;
|
||||
constexpr Elf64_Sxword DT_SYMENT = 0x0000000b;
|
||||
constexpr Elf64_Sxword DT_PLTGOT = 0x00000003;
|
||||
constexpr Elf64_Sxword DT_PLTREL = 0x00000014;
|
||||
constexpr Elf64_Sxword DT_JMPREL = 0x00000017;
|
||||
constexpr Elf64_Sxword DT_PLTRELSZ = 0x00000002;
|
||||
constexpr Elf64_Sxword DT_RELASZ = 0x00000008;
|
||||
constexpr Elf64_Sxword DT_RELAENT = 0x00000009;
|
||||
constexpr Elf64_Sxword DT_RELACOUNT = 0x6ffffff9;
|
||||
|
||||
constexpr Elf64_Sxword DT_NULL = 0;
|
||||
|
||||
constexpr Elf64_Word R_X86_64_64 = 1;
|
||||
constexpr Elf64_Word R_X86_64_GLOB_DAT = 6;
|
||||
constexpr Elf64_Word R_X86_64_JUMP_SLOT = 7;
|
||||
constexpr Elf64_Word R_X86_64_RELATIVE = 8;
|
||||
constexpr Elf64_Word R_X86_64_DTPMOD64 = 16;
|
||||
|
||||
constexpr uint8_t STB_LOCAL = 0;
|
||||
constexpr uint8_t STB_GLOBAL = 1;
|
||||
constexpr uint8_t STB_WEAK = 2;
|
||||
|
||||
constexpr uint8_t STT_OBJECT = 1;
|
||||
constexpr uint8_t STT_FUNC = 2;
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct Elf64_Ehdr // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
unsigned char e_ident[EI_NIDENT]; /* ELF identification */
|
||||
Elf64_Half e_type; /* Object file type */
|
||||
Elf64_Half e_machine; /* Machine type */
|
||||
Elf64_Word e_version; /* Object file version */
|
||||
Elf64_Addr e_entry; /* Entry point address */
|
||||
Elf64_Off e_phoff; /* Program header offset */
|
||||
Elf64_Off e_shoff; /* Section header offset */
|
||||
Elf64_Word e_flags; /* Processor-specific flags */
|
||||
Elf64_Half e_ehsize; /* ELF header size */
|
||||
Elf64_Half e_phentsize; /* Size of program header entry */
|
||||
Elf64_Half e_phnum; /* Number of program header entries */
|
||||
Elf64_Half e_shentsize; /* Size of section header entry */
|
||||
Elf64_Half e_shnum; /* Number of section header entries */
|
||||
Elf64_Half e_shstrndx; /* Section name string table index */
|
||||
};
|
||||
|
||||
struct Elf64_Phdr // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
Elf64_Word p_type; /* Type of segment */
|
||||
Elf64_Word p_flags; /* Segment attributes */
|
||||
Elf64_Off p_offset; /* Offset in file */
|
||||
Elf64_Addr p_vaddr; /* Virtual address in memory */
|
||||
Elf64_Addr p_paddr; /* Reserved */
|
||||
Elf64_Xword p_filesz; /* Size of segment in file */
|
||||
Elf64_Xword p_memsz; /* Size of segment in memory */
|
||||
Elf64_Xword p_align; /* Alignment of segment */
|
||||
};
|
||||
|
||||
struct Elf64_Shdr // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
Elf64_Word sh_name; /* Section name */
|
||||
Elf64_Word sh_type; /* Section type */
|
||||
Elf64_Xword sh_flags; /* Section attributes */
|
||||
Elf64_Addr sh_addr; /* Virtual address in memory */
|
||||
Elf64_Off sh_offset; /* Offset in file */
|
||||
Elf64_Xword sh_size; /* Size of section */
|
||||
Elf64_Word sh_link; /* Link to other section */
|
||||
Elf64_Word sh_info; /* Miscellaneous information */
|
||||
Elf64_Xword sh_addralign; /* Address alignment boundary */
|
||||
Elf64_Xword sh_entsize; /* Size of entries, if section has table */
|
||||
};
|
||||
|
||||
struct Elf64_Dyn // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
Elf64_Sxword d_tag;
|
||||
union
|
||||
{
|
||||
Elf64_Xword d_val;
|
||||
Elf64_Addr d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
|
||||
struct Elf64_Sym // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
[[nodiscard]] unsigned char GetBind() const { return st_info >> 4u; }
|
||||
[[nodiscard]] unsigned char GetType() const { return st_info & 0xfu; }
|
||||
|
||||
Elf64_Word st_name;
|
||||
unsigned char st_info;
|
||||
unsigned char st_other;
|
||||
Elf64_Half st_shndx;
|
||||
Elf64_Addr st_value;
|
||||
Elf64_Xword st_size;
|
||||
};
|
||||
|
||||
// struct Elf64_Rel // NOLINT(readability-identifier-naming)
|
||||
//{
|
||||
// Elf64_Addr r_offset;
|
||||
// Elf64_Xword r_info;
|
||||
//};
|
||||
|
||||
struct Elf64_Rela // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
[[nodiscard]] Elf64_Word GetSymbol() const { return static_cast<Elf64_Word>(r_info >> 32u); }
|
||||
[[nodiscard]] Elf64_Word GetType() const { return static_cast<Elf64_Word>(r_info & 0xffffffff); }
|
||||
|
||||
Elf64_Addr r_offset;
|
||||
Elf64_Xword r_info;
|
||||
Elf64_Sxword r_addend;
|
||||
};
|
||||
|
||||
struct tls_info_t // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
uint64_t addr;
|
||||
uint64_t filesz;
|
||||
uint64_t memsz;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
class Elf64
|
||||
{
|
||||
public:
|
||||
Elf64() = default;
|
||||
virtual ~Elf64();
|
||||
|
||||
void Open(const String& file_name);
|
||||
|
||||
void DbgDump(const String& folder);
|
||||
|
||||
const char* GetSectionName(int index) { return m_str_table + m_shdr[index].sh_name; }
|
||||
|
||||
[[nodiscard]] bool IsValid() const;
|
||||
[[nodiscard]] bool IsShared() const;
|
||||
[[nodiscard]] bool IsNextGen() const;
|
||||
|
||||
void LoadSegment(uint64_t vaddr, uint64_t file_offset, uint64_t size);
|
||||
|
||||
uint64_t GetEntry();
|
||||
|
||||
[[nodiscard]] const Elf64_Dyn* GetDynValue(Elf64_Sxword tag) const;
|
||||
[[nodiscard]] Vector<const Elf64_Dyn*> GetDynList(Elf64_Sxword tag) const;
|
||||
[[nodiscard]] bool HasDynValue(Elf64_Sxword tag) const { return GetDynValue(tag) != nullptr; }
|
||||
|
||||
KYTY_CLASS_NO_COPY(Elf64);
|
||||
|
||||
[[nodiscard]] const Elf64_Dyn* GetDynamic() const { return static_cast<Elf64_Dyn*>(m_dynamic); }
|
||||
[[nodiscard]] const Elf64_Ehdr* GetEhdr() const { return m_ehdr; }
|
||||
[[nodiscard]] const Elf64_Phdr* GetPhdr() const { return m_phdr; }
|
||||
[[nodiscard]] const Elf64_Shdr* GetShdr() const { return m_shdr; }
|
||||
[[nodiscard]] char* GetStrTable() const { return m_str_table; }
|
||||
|
||||
template <class T>
|
||||
[[nodiscard]] T GetDynamicData(uint64_t offset) const
|
||||
{
|
||||
return (m_dynamic_data == nullptr ? nullptr : reinterpret_cast<T>(static_cast<uint8_t*>(m_dynamic_data) + offset));
|
||||
}
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
|
||||
Core::File* m_f = nullptr;
|
||||
Elf64_Ehdr* m_ehdr = nullptr;
|
||||
Elf64_Phdr* m_phdr = nullptr;
|
||||
Elf64_Shdr* m_shdr = nullptr;
|
||||
void* m_dynamic = nullptr;
|
||||
void* m_dynamic_data = nullptr;
|
||||
char* m_str_table = nullptr;
|
||||
// uint64_t m_base_vaddr = 0;
|
||||
};
|
||||
|
||||
} // namespace Kyty::Loader
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_ELF_H_ */
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_EMULATOR_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_EMULATOR_H_
|
||||
|
||||
#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
namespace Kyty::Emulator {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Emulator);
|
||||
|
||||
} // namespace Kyty::Emulator
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_EMULATOR_H_ */
|
||||
@@ -0,0 +1,94 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_ASYNCJOB_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_ASYNCJOB_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Profiler.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
class AsyncJob
|
||||
{
|
||||
public:
|
||||
using func_t = std::function<void(void*)>;
|
||||
explicit AsyncJob(const char* name): m_name(name) { m_thread = new Core::Thread(ThreadRun, this); }
|
||||
virtual ~AsyncJob()
|
||||
{
|
||||
EXIT_IF(m_thread == nullptr);
|
||||
Core::LockGuard lock(m_mutex);
|
||||
m_need_exit = true;
|
||||
m_cond_var1.Signal();
|
||||
m_thread->Join();
|
||||
delete m_thread;
|
||||
}
|
||||
KYTY_CLASS_NO_COPY(AsyncJob);
|
||||
|
||||
void Execute(func_t func, void* arg = nullptr)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
while (m_func != nullptr)
|
||||
{
|
||||
m_cond_var2.Wait(&m_mutex);
|
||||
}
|
||||
m_func = std::move(func);
|
||||
m_arg = arg;
|
||||
m_cond_var1.Signal();
|
||||
}
|
||||
|
||||
void Wait()
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
while (m_func != nullptr)
|
||||
{
|
||||
m_cond_var2.Wait(&m_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Core::Mutex m_mutex;
|
||||
Core::CondVar m_cond_var1;
|
||||
Core::CondVar m_cond_var2;
|
||||
Core::Thread* m_thread = nullptr;
|
||||
const char* m_name = nullptr;
|
||||
|
||||
func_t m_func = nullptr;
|
||||
void* m_arg = nullptr;
|
||||
bool m_need_exit = false;
|
||||
|
||||
static void ThreadRun(void* data)
|
||||
{
|
||||
// printf("Start AsyncJob Thread: 0x%" PRIx64 "\n", static_cast<uint64_t>(GetCurrentThreadId()));
|
||||
auto* aj = static_cast<AsyncJob*>(data);
|
||||
|
||||
if (aj->m_name != nullptr)
|
||||
{
|
||||
KYTY_PROFILER_THREAD(aj->m_name);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
Core::LockGuard lock(aj->m_mutex);
|
||||
while (aj->m_func == nullptr && !aj->m_need_exit)
|
||||
{
|
||||
aj->m_cond_var1.Wait(&aj->m_mutex);
|
||||
}
|
||||
if (aj->m_need_exit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
aj->m_func(aj->m_arg);
|
||||
aj->m_func = nullptr;
|
||||
aj->m_cond_var2.Signal();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_ASYNCJOB_H_ */
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_DEPTHSTENCILBUFFER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_DEPTHSTENCILBUFFER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
struct VulkanMemory;
|
||||
|
||||
class DepthStencilBufferObject: public GpuObject
|
||||
{
|
||||
public:
|
||||
static constexpr int PARAM_FORMAT = 0;
|
||||
static constexpr int PARAM_WIDTH = 1;
|
||||
static constexpr int PARAM_HEIGHT = 2;
|
||||
static constexpr int PARAM_HTILE = 3;
|
||||
static constexpr int PARAM_NEO = 4;
|
||||
|
||||
DepthStencilBufferObject(uint64_t vk_format, uint32_t width, uint32_t height, bool htile, bool neo)
|
||||
{
|
||||
params[PARAM_FORMAT] = vk_format;
|
||||
params[PARAM_WIDTH] = width;
|
||||
params[PARAM_HEIGHT] = height;
|
||||
params[PARAM_HTILE] = htile ? 1 : 0;
|
||||
params[PARAM_NEO] = neo ? 1 : 0;
|
||||
check_hash = false;
|
||||
type = Graphics::GpuMemoryObjectType::DepthStencilBuffer;
|
||||
}
|
||||
|
||||
void* Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const override;
|
||||
bool Equal(const uint64_t* other) const override;
|
||||
|
||||
[[nodiscard]] write_back_func_t GetWriteBackFunc() const override { return nullptr; };
|
||||
[[nodiscard]] delete_func_t GetDeleteFunc() const override;
|
||||
[[nodiscard]] update_func_t GetUpdateFunc() const override;
|
||||
};
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_DEPTHSTENCILBUFFER_H_ */
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GPUMEMORY_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GPUMEMORY_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
struct VulkanMemory;
|
||||
struct VulkanBuffer;
|
||||
struct TextureVulkanImage;
|
||||
struct VideoOutVulkanImage;
|
||||
struct DepthStencilVulkanImage;
|
||||
|
||||
enum class GpuMemoryMode
|
||||
{
|
||||
NoAccess,
|
||||
Read,
|
||||
Write,
|
||||
ReadWrite
|
||||
};
|
||||
|
||||
enum class GpuMemoryObjectType
|
||||
{
|
||||
Invalid,
|
||||
VideoOutBuffer,
|
||||
DepthStencilBuffer,
|
||||
Label,
|
||||
IndexBuffer,
|
||||
VertexBuffer,
|
||||
StorageBuffer,
|
||||
Texture
|
||||
};
|
||||
|
||||
class GpuObject
|
||||
{
|
||||
public:
|
||||
using write_back_func_t = void (*)(GraphicContext* ctx, void* obj, const uint64_t* vaddr, const uint64_t* size, int vaddr_num);
|
||||
using delete_func_t = void (*)(GraphicContext* ctx, void* obj, VulkanMemory* mem);
|
||||
using update_func_t = void (*)(GraphicContext* ctx, const uint64_t* params, void* obj, const uint64_t* vaddr, const uint64_t* size,
|
||||
int vaddr_num);
|
||||
|
||||
static constexpr int PARAMS_MAX = 8;
|
||||
|
||||
GpuObject() = default;
|
||||
virtual ~GpuObject() = default;
|
||||
|
||||
KYTY_CLASS_DEFAULT_COPY(GpuObject);
|
||||
|
||||
virtual void* Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const = 0;
|
||||
virtual bool Equal(const uint64_t* other) const = 0;
|
||||
|
||||
[[nodiscard]] virtual write_back_func_t GetWriteBackFunc() const = 0;
|
||||
[[nodiscard]] virtual delete_func_t GetDeleteFunc() const = 0;
|
||||
[[nodiscard]] virtual update_func_t GetUpdateFunc() const = 0;
|
||||
|
||||
uint64_t params[PARAMS_MAX] = {};
|
||||
bool check_hash = false;
|
||||
bool read_only = false;
|
||||
GpuMemoryObjectType type = GpuMemoryObjectType::Invalid;
|
||||
};
|
||||
|
||||
void GpuMemoryInit();
|
||||
|
||||
void GpuMemorySetAllocatedRange(uint64_t vaddr, uint64_t size);
|
||||
void GpuMemoryFree(GraphicContext* ctx, uint64_t vaddr, uint64_t size);
|
||||
void* GpuMemoryGetObject(GraphicContext* ctx, uint64_t vaddr, uint64_t size, const GpuObject& info);
|
||||
void* GpuMemoryGetObject(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, const GpuObject& info);
|
||||
void GpuMemoryResetHash(GraphicContext* ctx, uint64_t vaddr, uint64_t size, GpuMemoryObjectType type);
|
||||
void GpuMemoryDbgDump();
|
||||
void GpuMemoryFlush();
|
||||
void GpuMemoryFrameDone();
|
||||
void GpuMemoryWriteBack(GraphicContext* ctx);
|
||||
|
||||
bool VulkanAllocate(GraphicContext* ctx, VulkanMemory* mem);
|
||||
void VulkanFree(GraphicContext* ctx, VulkanMemory* mem);
|
||||
void VulkanMapMemory(GraphicContext* ctx, VulkanMemory* mem, void** data);
|
||||
void VulkanUnmapMemory(GraphicContext* ctx, VulkanMemory* mem);
|
||||
void VulkanBindImageMemory(GraphicContext* ctx, TextureVulkanImage* image, VulkanMemory* mem);
|
||||
void VulkanBindImageMemory(GraphicContext* ctx, VideoOutVulkanImage* image, VulkanMemory* mem);
|
||||
void VulkanBindImageMemory(GraphicContext* ctx, DepthStencilVulkanImage* image, VulkanMemory* mem);
|
||||
void VulkanBindBufferMemory(GraphicContext* ctx, VulkanBuffer* buffer, VulkanMemory* mem);
|
||||
|
||||
void GpuMemoryRegisterOwner(uint32_t* owner_handle, const char* 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);
|
||||
void GpuMemoryUnregisterAllResourcesForOwner(uint32_t owner_handle);
|
||||
void GpuMemoryUnregisterOwnerAndResources(uint32_t owner_handle);
|
||||
void GpuMemoryUnregisterResource(uint32_t resource_handle);
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GPUMEMORY_H_ */
|
||||
@@ -0,0 +1,106 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICCONTEXT_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICCONTEXT_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct VulkanSwapchain
|
||||
{
|
||||
VkSwapchainKHR swapchain = nullptr;
|
||||
VkFormat swapchain_format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D swapchain_extent = {};
|
||||
VkImage* swapchain_images = nullptr;
|
||||
VkImageView* swapchain_image_views = nullptr;
|
||||
uint32_t swapchain_images_count = 0;
|
||||
VkSemaphore present_complete_semaphore = nullptr;
|
||||
VkFence present_complete_fence = nullptr;
|
||||
uint32_t current_index = 0;
|
||||
};
|
||||
|
||||
struct VulkanCommandPool
|
||||
{
|
||||
Core::Mutex mutex;
|
||||
VkCommandPool pool = nullptr;
|
||||
VkCommandBuffer* buffers = nullptr;
|
||||
VkFence* fences = nullptr;
|
||||
VkSemaphore* semaphores = nullptr;
|
||||
bool* busy = nullptr;
|
||||
uint32_t buffers_count = 0;
|
||||
};
|
||||
|
||||
struct GraphicContext
|
||||
{
|
||||
static constexpr int QUEUES_NUM = 11;
|
||||
static constexpr int QUEUE_GFX = 8;
|
||||
static constexpr int QUEUE_UTIL = 9;
|
||||
static constexpr int QUEUE_PRESENT = 10;
|
||||
static constexpr int QUEUE_COMPUTE_START = 0;
|
||||
static constexpr int QUEUE_COMPUTE_NUM = 8;
|
||||
|
||||
uint32_t screen_width = 0;
|
||||
uint32_t screen_height = 0;
|
||||
VkInstance instance = nullptr;
|
||||
VkDebugUtilsMessengerEXT debug_messenger = nullptr;
|
||||
VkPhysicalDevice physical_device = nullptr;
|
||||
VkDevice device = nullptr;
|
||||
uint32_t queue_family_index = static_cast<uint32_t>(-1);
|
||||
VkQueue queue[QUEUES_NUM] = {};
|
||||
};
|
||||
|
||||
struct VulkanMemory
|
||||
{
|
||||
VkMemoryRequirements requirements = {};
|
||||
VkMemoryPropertyFlags property = 0;
|
||||
VkDeviceMemory memory = nullptr;
|
||||
VkDeviceSize offset = 0;
|
||||
uint32_t type = 0;
|
||||
uint64_t unique_id = 0;
|
||||
};
|
||||
|
||||
struct VideoOutVulkanImage
|
||||
{
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D extent = {};
|
||||
VkImage image = nullptr;
|
||||
VkImageView image_view = nullptr;
|
||||
Graphics::VulkanMemory memory;
|
||||
};
|
||||
|
||||
struct DepthStencilVulkanImage
|
||||
{
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D extent = {};
|
||||
VkImage image = nullptr;
|
||||
VkImageView image_view = nullptr;
|
||||
Graphics::VulkanMemory memory;
|
||||
};
|
||||
|
||||
struct TextureVulkanImage
|
||||
{
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D extent = {};
|
||||
VkImage image = nullptr;
|
||||
VkImageView image_view = nullptr;
|
||||
Graphics::VulkanMemory memory;
|
||||
};
|
||||
|
||||
struct VulkanBuffer
|
||||
{
|
||||
VkBuffer buffer = nullptr;
|
||||
VulkanMemory memory;
|
||||
VkBufferUsageFlags usage = 0;
|
||||
};
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICCONTEXT_H_ */
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICS_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICS_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Kernel/EventQueue.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct VsStageRegisters;
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Graphics);
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsSetVsShader(uint32_t* cmd, uint64_t size, const VsStageRegisters* vs_regs, uint32_t shader_modifier);
|
||||
int KYTY_SYSV_ABI GraphicsSetPsShader350(uint32_t* cmd, uint64_t size, const uint32_t* ps_regs);
|
||||
int KYTY_SYSV_ABI GraphicsSetCsShaderWithModifier(uint32_t* cmd, uint64_t size, const uint32_t* cs_regs, uint32_t shader_modifier);
|
||||
int KYTY_SYSV_ABI GraphicsSetEmbeddedVsShader(uint32_t* cmd, uint64_t size, uint32_t id, uint32_t shader_modifier);
|
||||
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);
|
||||
int KYTY_SYSV_ABI GraphicsDrawIndexAuto(uint32_t* cmd, uint64_t size, uint32_t index_count, uint32_t flags);
|
||||
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);
|
||||
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);
|
||||
int KYTY_SYSV_ABI GraphicsSubmitDone();
|
||||
void KYTY_SYSV_ABI GraphicsFlushMemory();
|
||||
int KYTY_SYSV_ABI GraphicsAddEqEvent(LibKernel::EventQueue::KernelEqueue eq, int id, void* udata);
|
||||
int KYTY_SYSV_ABI GraphicsDeleteEqEvent(LibKernel::EventQueue::KernelEqueue eq, int id);
|
||||
uint32_t KYTY_SYSV_ABI GraphicsDrawInitDefaultHardwareState350(uint32_t* cmd, uint64_t size);
|
||||
uint32_t KYTY_SYSV_ABI GraphicsDispatchInitDefaultHardwareState(uint32_t* cmd, uint64_t size);
|
||||
int KYTY_SYSV_ABI GraphicsInsertWaitFlipDone(uint32_t* cmd, uint64_t size, uint32_t video_out_handle, uint32_t display_buffer_index);
|
||||
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);
|
||||
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);
|
||||
int KYTY_SYSV_ABI GraphicsComputeWaitOnAddress(uint32_t* cmd, uint64_t size, uint32_t* gpu_addr, uint32_t mask, uint32_t func,
|
||||
uint32_t ref);
|
||||
void KYTY_SYSV_ABI GraphicsDingDong(uint32_t ring_id, uint32_t offset_dw);
|
||||
void KYTY_SYSV_ABI GraphicsUnmapComputeQueue(uint32_t id);
|
||||
int KYTY_SYSV_ABI GraphicsInsertPushMarker(uint32_t* cmd, uint64_t size, const char* str);
|
||||
int KYTY_SYSV_ABI GraphicsInsertPopMarker(uint32_t* cmd, uint64_t size);
|
||||
uint64_t KYTY_SYSV_ABI GraphicsGetGpuCoreClockFrequency();
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsRegisterOwner(uint32_t* owner_handle, const char* name);
|
||||
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);
|
||||
int KYTY_SYSV_ABI GraphicsUnregisterAllResourcesForOwner(uint32_t owner_handle);
|
||||
int KYTY_SYSV_ABI GraphicsUnregisterOwnerAndResources(uint32_t owner_handle);
|
||||
int KYTY_SYSV_ABI GraphicsUnregisterResource(uint32_t resource_handle);
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICS_H_ */
|
||||
@@ -0,0 +1,92 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICSRENDER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICSRENDER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Kernel/EventQueue.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
class HardwareContext;
|
||||
class UserConfig;
|
||||
struct VideoOutVulkanImage;
|
||||
struct DepthStencilVulkanImage;
|
||||
struct TextureVulkanImage;
|
||||
struct VulkanCommandPool;
|
||||
struct VulkanBuffer;
|
||||
struct VulkanFramebuffer;
|
||||
struct RenderDepthInfo;
|
||||
struct RenderColorInfo;
|
||||
|
||||
class CommandBuffer
|
||||
{
|
||||
public:
|
||||
CommandBuffer() { Allocate(); }
|
||||
virtual ~CommandBuffer() { Free(); }
|
||||
|
||||
KYTY_CLASS_NO_COPY(CommandBuffer);
|
||||
|
||||
[[nodiscard]] bool IsInvalid() const;
|
||||
|
||||
void Allocate();
|
||||
void Free();
|
||||
void Begin() const;
|
||||
void End() const;
|
||||
void Execute();
|
||||
void ExecuteWithSemaphore();
|
||||
VulkanFramebuffer* BeginRenderPass(RenderColorInfo* color, RenderDepthInfo* depth) const;
|
||||
void EndRenderPass() const;
|
||||
void WaitForFence();
|
||||
void WaitForFenceAndReset();
|
||||
|
||||
[[nodiscard]] uint32_t GetIndex() const { return m_index; }
|
||||
VulkanCommandPool* GetPool() { return m_pool; }
|
||||
[[nodiscard]] bool IsExecute() const { return m_execute; }
|
||||
|
||||
void SetQueue(int queue) { m_queue = queue; }
|
||||
|
||||
private:
|
||||
VulkanCommandPool* m_pool = nullptr;
|
||||
uint32_t m_index = static_cast<uint32_t>(-1);
|
||||
int m_queue = -1;
|
||||
bool m_execute = false;
|
||||
};
|
||||
|
||||
void GraphicsRenderInit();
|
||||
void GraphicsRenderCreateContext();
|
||||
|
||||
void GraphicsRenderDrawIndex(CommandBuffer* buffer, HardwareContext* ctx, UserConfig* ucfg, uint32_t index_type_and_size,
|
||||
uint32_t index_count, const void* index_addr, uint32_t flags, uint32_t type);
|
||||
void GraphicsRenderDrawIndexAuto(CommandBuffer* buffer, HardwareContext* ctx, UserConfig* ucfg, uint32_t index_count, uint32_t flags);
|
||||
void GraphicsRenderWriteAtEndOfPipe(CommandBuffer* buffer, uint64_t* dst_gpu_addr, uint64_t value);
|
||||
void GraphicsRenderWriteAtEndOfPipeClockCounter(CommandBuffer* buffer, uint64_t* dst_gpu_addr);
|
||||
void GraphicsRenderWriteAtEndOfPipe(CommandBuffer* buffer, uint32_t* dst_gpu_addr, uint32_t value);
|
||||
void GraphicsRenderWriteAtEndOfPipeGds(CommandBuffer* buffer, uint32_t* dst_gpu_addr, uint32_t dw_offset, uint32_t dw_num);
|
||||
void GraphicsRenderWriteAtEndOfPipeWithInterruptWriteBackFlip(CommandBuffer* buffer, uint32_t* dst_gpu_addr, uint32_t value, int handle,
|
||||
int index, int flip_mode, int64_t flip_arg);
|
||||
void GraphicsRenderWriteAtEndOfPipeWithWriteBack(CommandBuffer* buffer, uint64_t* dst_gpu_addr, uint64_t value);
|
||||
void GraphicsRenderWriteAtEndOfPipeWithInterrupt(CommandBuffer* buffer, uint64_t* dst_gpu_addr, uint64_t value);
|
||||
void GraphicsRenderWriteBack();
|
||||
void GraphicsRenderDispatchDirect(CommandBuffer* buffer, HardwareContext* ctx, uint32_t thread_group_x, uint32_t thread_group_y,
|
||||
uint32_t thread_group_z, uint32_t mode);
|
||||
void GraphicsRenderMemoryBarrier(CommandBuffer* buffer);
|
||||
|
||||
void DeleteFramebuffer(VideoOutVulkanImage* image);
|
||||
void DeleteFramebuffer(DepthStencilVulkanImage* image);
|
||||
void DeleteDescriptor(VulkanBuffer* buffer);
|
||||
void DeleteDescriptor(TextureVulkanImage* image);
|
||||
|
||||
int GraphicsRenderAddEqEvent(LibKernel::EventQueue::KernelEqueue eq, int id, void* udata);
|
||||
int GraphicsRenderDeleteEqEvent(LibKernel::EventQueue::KernelEqueue eq, int id);
|
||||
|
||||
void GraphicsRenderClearGds(uint64_t dw_offset, uint32_t dw_num, uint32_t clear_value);
|
||||
void GraphicsRenderReadGds(uint32_t* dst, uint32_t dw_offset, uint32_t dw_size);
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICSRENDER_H_ */
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICSRUN_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICSRUN_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
void GraphicsRunInit();
|
||||
|
||||
void GraphicsRunSubmit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer, uint32_t num_const_dw);
|
||||
void GraphicsRunSubmitAndFlip(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer, uint32_t num_const_dw,
|
||||
int handle, int index, int flip_mode, int64_t flip_arg);
|
||||
uint32_t GraphicsRunMapComputeQueue(uint32_t pipe_id, uint32_t queue_id, uint32_t* ring_addr, uint32_t ring_size_dw,
|
||||
uint32_t* read_ptr_addr);
|
||||
void GraphicsRunUnmapComputeQueue(uint32_t id);
|
||||
void GraphicsRunWait();
|
||||
void GraphicsRunDone();
|
||||
void GraphicsRunDingDong(uint32_t ring_id, uint32_t offset_dw);
|
||||
int GraphicsRunGetFrameNum();
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GRAPHICSRUN_H_ */
|
||||
@@ -0,0 +1,511 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HARDWARECONTEXT_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HARDWARECONTEXT_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct RenderTarget
|
||||
{
|
||||
uint64_t base_addr = 0;
|
||||
uint32_t pitch_div8_minus1 = 0;
|
||||
uint32_t fmask_pitch_div8_minus1 = 0;
|
||||
uint32_t slice_div64_minus1 = 0;
|
||||
uint32_t base_array_slice_index = 0;
|
||||
uint32_t last_array_slice_index = 0;
|
||||
bool fmask_compression_enable = false;
|
||||
uint32_t fmask_compression_mode = 0;
|
||||
bool cmask_fast_clear_enable = false;
|
||||
bool dcc_compression_enable = false;
|
||||
bool neo_mode = false;
|
||||
uint32_t cmask_tile_mode = 0;
|
||||
uint32_t cmask_tile_mode_neo = 0;
|
||||
uint32_t format = 0;
|
||||
uint32_t channel_type = 0;
|
||||
uint32_t channel_order = 0;
|
||||
bool force_dest_alpha_to_one = false;
|
||||
uint32_t tile_mode = 0;
|
||||
uint32_t fmask_tile_mode = 0;
|
||||
uint32_t num_samples = 0;
|
||||
uint32_t num_fragments = 0;
|
||||
uint32_t dcc_max_uncompressed_block_size = 0;
|
||||
uint32_t dcc_max_compressed_block_size = 0;
|
||||
uint32_t dcc_min_compressed_block_size = 0;
|
||||
uint32_t dcc_color_transform = 0;
|
||||
bool dcc_enable_overwrite_combiner = false;
|
||||
bool dcc_force_independent_blocks = false;
|
||||
uint64_t cmask_addr = 0;
|
||||
uint32_t cmask_slice_minus1 = 0;
|
||||
uint64_t fmask_addr = 0;
|
||||
uint32_t fmask_slice_minus1 = 0;
|
||||
uint32_t clear_color_word0 = 0;
|
||||
uint32_t clear_color_word1 = 0;
|
||||
uint64_t dcc_addr = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
};
|
||||
|
||||
struct DepthRenderTargetZInfo
|
||||
{
|
||||
uint32_t format = 0;
|
||||
uint32_t tile_mode_index = 0;
|
||||
uint32_t num_samples = 0;
|
||||
bool tile_surface_enable = false;
|
||||
bool expclear_enabled = false;
|
||||
uint32_t zrange_precision = 0;
|
||||
};
|
||||
|
||||
struct DepthRenderTargetStencilInfo
|
||||
{
|
||||
uint32_t format = 0;
|
||||
uint32_t tile_mode_index = 0;
|
||||
uint32_t tile_split = 0;
|
||||
bool expclear_enabled = false;
|
||||
bool tile_stencil_disable = false;
|
||||
};
|
||||
|
||||
struct DepthRenderTargetDepthInfo
|
||||
{
|
||||
uint32_t addr5_swizzle_mask = 0;
|
||||
uint32_t array_mode = 0;
|
||||
uint32_t pipe_config = 0;
|
||||
uint32_t bank_width = 0;
|
||||
uint32_t bank_height = 0;
|
||||
uint32_t macro_tile_aspect = 0;
|
||||
uint32_t num_banks = 0;
|
||||
};
|
||||
|
||||
struct DepthRenderTargetDepthView
|
||||
{
|
||||
uint32_t slice_start = 0;
|
||||
uint32_t slice_max = 0;
|
||||
};
|
||||
|
||||
struct DepthRenderTargetHTileSurface
|
||||
{
|
||||
uint32_t linear = 0;
|
||||
uint32_t full_cache = 0;
|
||||
uint32_t htile_uses_preload_win = 0;
|
||||
uint32_t preload = 0;
|
||||
uint32_t prefetch_width = 0;
|
||||
uint32_t prefetch_height = 0;
|
||||
uint32_t dst_outside_zero_to_one = 0;
|
||||
};
|
||||
|
||||
struct DepthRenderTarget
|
||||
{
|
||||
DepthRenderTargetZInfo z_info;
|
||||
DepthRenderTargetStencilInfo stencil_info;
|
||||
DepthRenderTargetDepthInfo depth_info;
|
||||
DepthRenderTargetDepthView depth_view;
|
||||
DepthRenderTargetHTileSurface htile_surface;
|
||||
|
||||
uint64_t z_read_base_addr = 0;
|
||||
uint64_t stencil_read_base_addr = 0;
|
||||
uint64_t z_write_base_addr = 0;
|
||||
uint64_t stencil_write_base_addr = 0;
|
||||
uint32_t pitch_div8_minus1 = 0;
|
||||
uint32_t height_div8_minus1 = 0;
|
||||
uint32_t slice_div64_minus1 = 0;
|
||||
uint64_t htile_data_base_addr = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
};
|
||||
|
||||
struct RenderControl
|
||||
{
|
||||
bool depth_clear_enable = false;
|
||||
bool stencil_clear_enable = false;
|
||||
bool resummarize_enable = false;
|
||||
bool stencil_compress_disable = false;
|
||||
bool depth_compress_disable = false;
|
||||
bool copy_centroid = false;
|
||||
uint8_t copy_sample = 0;
|
||||
};
|
||||
|
||||
struct ClipControl
|
||||
{
|
||||
uint32_t user_clip_planes = 0;
|
||||
uint32_t user_clip_plane_mode = 0;
|
||||
uint32_t clip_space = 0;
|
||||
uint32_t vertex_kill_mode = 0;
|
||||
uint32_t min_z_clip_enable = 0;
|
||||
uint32_t max_z_clip_enable = 0;
|
||||
bool user_clip_plane_negate_y = false;
|
||||
bool clip_enable = false;
|
||||
bool user_clip_plane_cull_only = false;
|
||||
bool cull_on_clipping_error_disable = false;
|
||||
bool linear_attribute_clip_enable = false;
|
||||
bool force_viewport_index_from_vs_enable = false;
|
||||
};
|
||||
|
||||
struct DepthControl
|
||||
{
|
||||
bool stencil_enable = false;
|
||||
bool z_enable = false;
|
||||
bool z_write_enable = false;
|
||||
bool depth_bounds_enable = false;
|
||||
uint8_t zfunc = 0;
|
||||
bool backface_enable = false;
|
||||
uint8_t stencilfunc = 0;
|
||||
uint8_t stencilfunc_bf = 0;
|
||||
};
|
||||
|
||||
struct ModeControl
|
||||
{
|
||||
bool cull_front = false;
|
||||
bool cull_back = false;
|
||||
bool face = false;
|
||||
uint8_t poly_mode = 0;
|
||||
uint8_t polymode_front_ptype = 0;
|
||||
uint8_t polymode_back_ptype = 0;
|
||||
bool poly_offset_front_enable = false;
|
||||
bool poly_offset_back_enable = false;
|
||||
bool vtx_window_offset_enable = false;
|
||||
bool provoking_vtx_last = false;
|
||||
bool persp_corr_dis = false;
|
||||
};
|
||||
|
||||
struct BlendControl
|
||||
{
|
||||
uint8_t color_srcblend = 0;
|
||||
uint8_t color_comb_fcn = 0;
|
||||
uint8_t color_destblend = 0;
|
||||
uint8_t alpha_srcblend = 0;
|
||||
uint8_t alpha_comb_fcn = 0;
|
||||
uint8_t alpha_destblend = 0;
|
||||
bool separate_alpha_blend = false;
|
||||
bool enable = false;
|
||||
};
|
||||
|
||||
struct Viewport
|
||||
{
|
||||
float zmin = 0.0f;
|
||||
float zmax = 0.0f;
|
||||
float xscale = 0.0f;
|
||||
float xoffset = 0.0f;
|
||||
float yscale = 0.0f;
|
||||
float yoffset = 0.0f;
|
||||
float zscale = 0.0f;
|
||||
float zoffset = 0.0f;
|
||||
};
|
||||
|
||||
struct ScreenViewport
|
||||
{
|
||||
Viewport viewports[15];
|
||||
uint32_t transform_control = 0;
|
||||
int scissor_left = 0;
|
||||
int scissor_top = 0;
|
||||
int scissor_right = 0;
|
||||
int scissor_bottom = 0;
|
||||
uint32_t hw_offset_x = 0;
|
||||
uint32_t hw_offset_y = 0;
|
||||
float guard_band_horz_clip = 0.0f;
|
||||
float guard_band_vert_clip = 0.0f;
|
||||
float guard_band_horz_discard = 0.0f;
|
||||
float guard_band_vert_discard = 0.0f;
|
||||
};
|
||||
|
||||
struct VsStageRegisters
|
||||
{
|
||||
uint32_t m_spiShaderPgmLoVs = 0;
|
||||
uint32_t m_spiShaderPgmHiVs = 0;
|
||||
uint32_t m_spiShaderPgmRsrc1Vs = 0;
|
||||
uint32_t m_spiShaderPgmRsrc2Vs = 0;
|
||||
|
||||
uint32_t m_spiVsOutConfig = 0;
|
||||
uint32_t m_spiShaderPosFormat = 0;
|
||||
uint32_t m_paClVsOutCntl = 0;
|
||||
|
||||
[[nodiscard]] uint64_t GetGpuAddress() const;
|
||||
[[nodiscard]] bool GetStreamoutEnabled() const;
|
||||
[[nodiscard]] uint32_t GetSgprCount() const;
|
||||
[[nodiscard]] uint32_t GetInputComponentsCount() const;
|
||||
[[nodiscard]] uint32_t GetUnknown1() const;
|
||||
[[nodiscard]] uint32_t GetUnknown2() const;
|
||||
};
|
||||
|
||||
struct PsStageRegisters
|
||||
{
|
||||
uint64_t data_addr = 0;
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t scratch_en = 0;
|
||||
uint8_t user_sgpr = 0;
|
||||
uint8_t wave_cnt_en = 0;
|
||||
uint32_t shader_z_format = 0;
|
||||
uint8_t target_output_mode[8] = {};
|
||||
uint32_t ps_input_ena = 0;
|
||||
uint32_t ps_input_addr = 0;
|
||||
uint32_t ps_in_control = 0;
|
||||
uint32_t baryc_cntl = 0;
|
||||
|
||||
uint8_t conservative_z_export_value = 0;
|
||||
uint8_t shader_z_behavior = 0;
|
||||
bool shader_kill_enable = false;
|
||||
bool shader_z_export_enable = false;
|
||||
bool shader_execute_on_noop = false;
|
||||
|
||||
uint32_t m_cbShaderMask = 0;
|
||||
};
|
||||
|
||||
struct CsStageRegisters
|
||||
{
|
||||
|
||||
uint64_t data_addr = 0;
|
||||
uint32_t num_thread_x = 0;
|
||||
uint32_t num_thread_y = 0;
|
||||
uint32_t num_thread_z = 0;
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t bulky = 0;
|
||||
uint8_t scratch_en = 0;
|
||||
uint8_t user_sgpr = 0;
|
||||
uint8_t tgid_x_en = 0;
|
||||
uint8_t tgid_y_en = 0;
|
||||
uint8_t tgid_z_en = 0;
|
||||
uint8_t tg_size_en = 0;
|
||||
uint8_t tidig_comp_cnt = 0;
|
||||
uint8_t lds_size = 0;
|
||||
};
|
||||
|
||||
enum class UserSgprType
|
||||
{
|
||||
Unknown,
|
||||
Region,
|
||||
Vsharp
|
||||
};
|
||||
|
||||
struct UserSgprInfo
|
||||
{
|
||||
uint32_t value[16] = {0};
|
||||
UserSgprType type[16] = {};
|
||||
uint32_t count = 0;
|
||||
};
|
||||
|
||||
struct VertexShaderInfo
|
||||
{
|
||||
VsStageRegisters vs_regs;
|
||||
uint32_t vs_shader_modifier = 0;
|
||||
uint32_t vs_embedded_id = 0;
|
||||
UserSgprInfo vs_user_sgpr;
|
||||
bool vs_embedded = false;
|
||||
};
|
||||
|
||||
struct PixelShaderInfo
|
||||
{
|
||||
PsStageRegisters ps_regs;
|
||||
uint32_t ps_interpolator_settings[32] = {0};
|
||||
uint32_t ps_input_num = 0;
|
||||
UserSgprInfo ps_user_sgpr;
|
||||
};
|
||||
|
||||
struct ComputeShaderInfo
|
||||
{
|
||||
CsStageRegisters cs_regs;
|
||||
uint32_t cs_shader_modifier = 0;
|
||||
UserSgprInfo cs_user_sgpr;
|
||||
};
|
||||
|
||||
class HardwareContext
|
||||
{
|
||||
public:
|
||||
HardwareContext() = default;
|
||||
virtual ~HardwareContext() = default;
|
||||
|
||||
KYTY_CLASS_DEFAULT_COPY(HardwareContext);
|
||||
|
||||
void Reset() { *this = HardwareContext(); }
|
||||
|
||||
void SetRenderTarget(uint32_t slot, const RenderTarget& target) { m_render_targets[slot] = target; }
|
||||
[[nodiscard]] const RenderTarget& GetRenderTargets(uint32_t slot) const { return m_render_targets[slot]; }
|
||||
|
||||
void SetBlendControl(uint32_t slot, const BlendControl& control) { m_blend_control[slot] = control; }
|
||||
[[nodiscard]] const BlendControl& GetBlendControl(uint32_t slot) const { return m_blend_control[slot]; }
|
||||
|
||||
void SetRenderTargetMask(uint32_t mask) { m_render_target_mask = mask; }
|
||||
[[nodiscard]] uint32_t GetRenderTargetMask() const { return m_render_target_mask; }
|
||||
|
||||
void SetShaderStages(uint32_t flags) { m_shader_stages = flags; }
|
||||
[[nodiscard]] uint32_t GetShaderStages() const { return m_shader_stages; }
|
||||
|
||||
void SetDepthRenderTarget(const DepthRenderTarget& target) { m_depth_render_target = target; }
|
||||
[[nodiscard]] const DepthRenderTarget& GetDepthRenderTarget() const { return m_depth_render_target; }
|
||||
void SetDepthRenderTargetZInfo(const DepthRenderTargetZInfo& info) { m_depth_render_target.z_info = info; }
|
||||
[[nodiscard]] const DepthRenderTargetZInfo& GetDepthRenderTargetZInfo() const { return m_depth_render_target.z_info; }
|
||||
void SetDepthRenderTargetStencilInfo(const DepthRenderTargetStencilInfo& info) { m_depth_render_target.stencil_info = info; }
|
||||
[[nodiscard]] const DepthRenderTargetStencilInfo& GetDepthRenderTargetStencilInfo() const { return m_depth_render_target.stencil_info; }
|
||||
|
||||
void SetViewportZ(uint32_t viewport_id, float zmin, float zmax)
|
||||
{
|
||||
m_screen_viewport.viewports[viewport_id].zmin = zmin;
|
||||
m_screen_viewport.viewports[viewport_id].zmax = zmax;
|
||||
}
|
||||
void SetViewportScaleOffset(uint32_t viewport_id, float xscale, float xoffset, float yscale, float yoffset, float zscale, float zoffset)
|
||||
{
|
||||
m_screen_viewport.viewports[viewport_id].xscale = xscale;
|
||||
m_screen_viewport.viewports[viewport_id].xoffset = xoffset;
|
||||
m_screen_viewport.viewports[viewport_id].yscale = yscale;
|
||||
m_screen_viewport.viewports[viewport_id].yoffset = yoffset;
|
||||
m_screen_viewport.viewports[viewport_id].zscale = zscale;
|
||||
m_screen_viewport.viewports[viewport_id].zoffset = zoffset;
|
||||
}
|
||||
void SetViewportTransformControl(uint32_t control) { m_screen_viewport.transform_control = control; }
|
||||
void SetScreenScissor(int left, int top, int right, int bottom)
|
||||
{
|
||||
m_screen_viewport.scissor_left = left;
|
||||
m_screen_viewport.scissor_top = top;
|
||||
m_screen_viewport.scissor_right = right;
|
||||
m_screen_viewport.scissor_bottom = bottom;
|
||||
}
|
||||
void SetHardwareScreenOffset(uint32_t offset_x, uint32_t offset_y)
|
||||
{
|
||||
m_screen_viewport.hw_offset_x = offset_x;
|
||||
m_screen_viewport.hw_offset_y = offset_y;
|
||||
}
|
||||
void SetGuardBands(float horz_clip, float vert_clip, float horz_discard, float vert_discard)
|
||||
{
|
||||
m_screen_viewport.guard_band_horz_clip = horz_clip;
|
||||
m_screen_viewport.guard_band_vert_clip = vert_clip;
|
||||
m_screen_viewport.guard_band_horz_discard = horz_discard;
|
||||
m_screen_viewport.guard_band_vert_discard = vert_discard;
|
||||
}
|
||||
[[nodiscard]] const ScreenViewport& GetScreenViewport() const { return m_screen_viewport; }
|
||||
|
||||
void SetVsShader(const VsStageRegisters* vs_regs, uint32_t shader_modifier)
|
||||
{
|
||||
m_vs.vs_regs = *vs_regs;
|
||||
m_vs.vs_shader_modifier = shader_modifier;
|
||||
m_vs.vs_embedded = false;
|
||||
}
|
||||
void SetVsEmbedded(uint32_t id, uint32_t shader_modifier)
|
||||
{
|
||||
m_vs.vs_embedded_id = id;
|
||||
m_vs.vs_shader_modifier = shader_modifier;
|
||||
m_vs.vs_embedded = true;
|
||||
}
|
||||
|
||||
void SetPsShader(const PsStageRegisters* ps_regs) { m_ps.ps_regs = *ps_regs; }
|
||||
|
||||
void SetCsShader(const CsStageRegisters* cs_regs, uint32_t shader_modifier)
|
||||
{
|
||||
m_cs.cs_regs = *cs_regs;
|
||||
m_cs.cs_shader_modifier = shader_modifier;
|
||||
}
|
||||
|
||||
[[nodiscard]] const ClipControl& GetClipControl() const { return m_clip_control; }
|
||||
void SetClipControl(const ClipControl& control) { m_clip_control = control; }
|
||||
[[nodiscard]] const RenderControl& GetRenderControl() const { return m_render_control; }
|
||||
void SetRenderControl(const RenderControl& control) { m_render_control = control; }
|
||||
[[nodiscard]] const DepthControl& GetDepthControl() const { return m_depth_control; }
|
||||
void SetDepthControl(const DepthControl& control) { m_depth_control = control; }
|
||||
[[nodiscard]] const ModeControl& GetModeControl() const { return m_mode_control; }
|
||||
void SetModeControl(const ModeControl& control) { m_mode_control = control; }
|
||||
|
||||
void SetVsUserSgpr(uint32_t id, uint32_t value, UserSgprType type)
|
||||
{
|
||||
m_vs.vs_user_sgpr.value[id] = value;
|
||||
m_vs.vs_user_sgpr.type[id] = type;
|
||||
m_vs.vs_user_sgpr.count = ((id + 1) > m_vs.vs_user_sgpr.count ? (id + 1) : m_vs.vs_user_sgpr.count);
|
||||
}
|
||||
void SetPsUserSgpr(uint32_t id, uint32_t value, UserSgprType type)
|
||||
{
|
||||
m_ps.ps_user_sgpr.value[id] = value;
|
||||
m_ps.ps_user_sgpr.type[id] = type;
|
||||
m_ps.ps_user_sgpr.count = ((id + 1) > m_ps.ps_user_sgpr.count ? (id + 1) : m_ps.ps_user_sgpr.count);
|
||||
}
|
||||
void SetCsUserSgpr(uint32_t id, uint32_t value, UserSgprType type)
|
||||
{
|
||||
m_cs.cs_user_sgpr.value[id] = value;
|
||||
m_cs.cs_user_sgpr.type[id] = type;
|
||||
m_cs.cs_user_sgpr.count = ((id + 1) > m_cs.cs_user_sgpr.count ? (id + 1) : m_cs.cs_user_sgpr.count);
|
||||
}
|
||||
void SetPsInputSettings(uint32_t id, uint32_t value)
|
||||
{
|
||||
m_ps.ps_interpolator_settings[id] = value;
|
||||
m_ps.ps_input_num = ((id + 1) > m_ps.ps_input_num ? (id + 1) : m_ps.ps_input_num);
|
||||
}
|
||||
|
||||
[[nodiscard]] const PixelShaderInfo& GetPs() const { return m_ps; }
|
||||
[[nodiscard]] const VertexShaderInfo& GetVs() const { return m_vs; }
|
||||
[[nodiscard]] const ComputeShaderInfo& GetCs() const { return m_cs; }
|
||||
|
||||
[[nodiscard]] float GetDepthClearValue() const { return m_depth_clear_value; }
|
||||
void SetDepthClearValue(float clear_value) { m_depth_clear_value = clear_value; }
|
||||
|
||||
private:
|
||||
BlendControl m_blend_control[8];
|
||||
RenderTarget m_render_targets[8];
|
||||
uint32_t m_render_target_mask = 0;
|
||||
ScreenViewport m_screen_viewport;
|
||||
ClipControl m_clip_control;
|
||||
|
||||
VertexShaderInfo m_vs;
|
||||
PixelShaderInfo m_ps;
|
||||
ComputeShaderInfo m_cs;
|
||||
uint32_t m_shader_stages = 0;
|
||||
|
||||
DepthRenderTarget m_depth_render_target;
|
||||
RenderControl m_render_control;
|
||||
DepthControl m_depth_control;
|
||||
float m_depth_clear_value = 0.0f;
|
||||
|
||||
ModeControl m_mode_control;
|
||||
};
|
||||
|
||||
class UserConfig
|
||||
{
|
||||
public:
|
||||
UserConfig() = default;
|
||||
virtual ~UserConfig() = default;
|
||||
|
||||
KYTY_CLASS_DEFAULT_COPY(UserConfig);
|
||||
|
||||
void Reset() { *this = UserConfig(); }
|
||||
|
||||
void SetPrimitiveType(uint32_t prim_type) { m_prim_type = prim_type; }
|
||||
[[nodiscard]] uint32_t GetPrimType() const { return m_prim_type; }
|
||||
|
||||
private:
|
||||
uint32_t m_prim_type = 0;
|
||||
};
|
||||
|
||||
inline uint64_t VsStageRegisters::GetGpuAddress() const
|
||||
{
|
||||
return (static_cast<uint64_t>(m_spiShaderPgmLoVs) << 8u) | (static_cast<uint64_t>(m_spiShaderPgmHiVs) << 40u);
|
||||
}
|
||||
|
||||
inline bool VsStageRegisters::GetStreamoutEnabled() const
|
||||
{
|
||||
return (m_spiShaderPgmRsrc2Vs & 0x00001000u) != 0u;
|
||||
}
|
||||
|
||||
inline uint32_t VsStageRegisters::GetSgprCount() const
|
||||
{
|
||||
return (m_spiShaderPgmRsrc1Vs >> 6u) & 0xfu;
|
||||
}
|
||||
|
||||
inline uint32_t VsStageRegisters::GetInputComponentsCount() const
|
||||
{
|
||||
return (m_spiShaderPgmRsrc1Vs >> 24u) & 0x3u;
|
||||
}
|
||||
|
||||
inline uint32_t VsStageRegisters::GetUnknown1() const
|
||||
{
|
||||
return m_spiShaderPgmRsrc1Vs & 0xfcfffc3fu;
|
||||
}
|
||||
|
||||
inline uint32_t VsStageRegisters::GetUnknown2() const
|
||||
{
|
||||
return m_spiShaderPgmRsrc2Vs & 0xFFFFEFFFu;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HARDWARECONTEXT_H_ */
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_INDEXBUFFER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_INDEXBUFFER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
struct VulkanMemory;
|
||||
|
||||
class IndexBufferGpuObject: public GpuObject
|
||||
{
|
||||
public:
|
||||
IndexBufferGpuObject()
|
||||
{
|
||||
check_hash = true;
|
||||
type = Graphics::GpuMemoryObjectType::IndexBuffer;
|
||||
}
|
||||
|
||||
void* Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const override;
|
||||
bool Equal(const uint64_t* other) const override;
|
||||
|
||||
[[nodiscard]] write_back_func_t GetWriteBackFunc() const override { return nullptr; };
|
||||
[[nodiscard]] delete_func_t GetDeleteFunc() const override;
|
||||
[[nodiscard]] update_func_t GetUpdateFunc() const override;
|
||||
};
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_INDEXBUFFER_H_ */
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_LABEL_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_LABEL_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct Label;
|
||||
struct GraphicContext;
|
||||
class CommandBuffer;
|
||||
struct VulkanMemory;
|
||||
|
||||
void LabelInit();
|
||||
|
||||
class LabelGpuObject: public GpuObject
|
||||
{
|
||||
public:
|
||||
using callback_t = bool (*)(const uint64_t* args);
|
||||
|
||||
static constexpr int PARAM_VALUE = 0;
|
||||
static constexpr int PARAM_CALLBACK_1 = 1;
|
||||
static constexpr int PARAM_CALLBACK_2 = 2;
|
||||
static constexpr int PARAM_ARG_1 = 3;
|
||||
static constexpr int PARAM_ARG_2 = 4;
|
||||
static constexpr int PARAM_ARG_3 = 5;
|
||||
static constexpr int PARAM_ARG_4 = 6;
|
||||
|
||||
explicit LabelGpuObject(uint64_t value, callback_t callback_1, callback_t callback_2, const uint64_t* args = nullptr)
|
||||
{
|
||||
params[PARAM_VALUE] = value;
|
||||
params[PARAM_CALLBACK_1] = reinterpret_cast<uint64_t>(callback_1);
|
||||
params[PARAM_CALLBACK_2] = reinterpret_cast<uint64_t>(callback_2);
|
||||
if (args != nullptr)
|
||||
{
|
||||
params[PARAM_ARG_1] = args[0];
|
||||
params[PARAM_ARG_2] = args[1];
|
||||
params[PARAM_ARG_3] = args[2];
|
||||
params[PARAM_ARG_4] = args[3];
|
||||
}
|
||||
check_hash = false;
|
||||
type = Graphics::GpuMemoryObjectType::Label;
|
||||
}
|
||||
|
||||
void* Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const override;
|
||||
bool Equal(const uint64_t* other) const override;
|
||||
|
||||
[[nodiscard]] write_back_func_t GetWriteBackFunc() const override { return nullptr; };
|
||||
[[nodiscard]] delete_func_t GetDeleteFunc() const override;
|
||||
[[nodiscard]] update_func_t GetUpdateFunc() const override;
|
||||
};
|
||||
|
||||
void LabelSet(CommandBuffer* buffer, Label* label);
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_LABEL_H_ */
|
||||
@@ -0,0 +1,315 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_PM4_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_PM4_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Core {
|
||||
class File;
|
||||
} // namespace Kyty::Core
|
||||
|
||||
#define KYTY_PM4_GET(u, r, f) (((u) >> Pm4::r##_##f##_SHIFT) & Pm4::r##_##f##_MASK)
|
||||
|
||||
#define KYTY_PM4(len, op, r) \
|
||||
(0xC0000000u | (((static_cast<uint16_t>(len) - 2u) & 0x3fffu) << 16u) | (((op)&0xffu) << 8u) | (((r)&0x3fu) << 2u))
|
||||
|
||||
namespace Kyty::Libs::Graphics::Pm4 {
|
||||
|
||||
constexpr uint32_t IT_NOP = 0x10;
|
||||
constexpr uint32_t IT_SET_BASE = 0x11;
|
||||
constexpr uint32_t IT_CLEAR_STATE = 0x12;
|
||||
constexpr uint32_t IT_INDEX_BUFFER_SIZE = 0x13;
|
||||
constexpr uint32_t IT_DISPATCH_DIRECT = 0x15;
|
||||
constexpr uint32_t IT_DISPATCH_INDIRECT = 0x16;
|
||||
constexpr uint32_t IT_SET_PREDICATION = 0x20;
|
||||
constexpr uint32_t IT_COND_EXEC = 0x22;
|
||||
constexpr uint32_t IT_DRAW_INDIRECT = 0x24;
|
||||
constexpr uint32_t IT_DRAW_INDEX_INDIRECT = 0x25;
|
||||
constexpr uint32_t IT_INDEX_BASE = 0x26;
|
||||
constexpr uint32_t IT_DRAW_INDEX_2 = 0x27;
|
||||
constexpr uint32_t IT_CONTEXT_CONTROL = 0x28;
|
||||
constexpr uint32_t IT_INDEX_TYPE = 0x2A;
|
||||
constexpr uint32_t IT_DRAW_INDIRECT_MULTI = 0x2C;
|
||||
constexpr uint32_t IT_DRAW_INDEX_AUTO = 0x2D;
|
||||
constexpr uint32_t IT_NUM_INSTANCES = 0x2F;
|
||||
constexpr uint32_t IT_INDIRECT_BUFFER_CNST = 0x33;
|
||||
constexpr uint32_t IT_DRAW_INDEX_OFFSET_2 = 0x35;
|
||||
constexpr uint32_t IT_WRITE_DATA = 0x37;
|
||||
constexpr uint32_t IT_MEM_SEMAPHORE = 0x39;
|
||||
constexpr uint32_t IT_DRAW_INDEX_INDIRECT_MULTI = 0x38;
|
||||
constexpr uint32_t IT_WAIT_REG_MEM = 0x3C;
|
||||
constexpr uint32_t IT_INDIRECT_BUFFER = 0x3F;
|
||||
constexpr uint32_t IT_COPY_DATA = 0x40;
|
||||
constexpr uint32_t IT_CP_DMA = 0x41;
|
||||
constexpr uint32_t IT_PFP_SYNC_ME = 0x42;
|
||||
constexpr uint32_t IT_SURFACE_SYNC = 0x43;
|
||||
constexpr uint32_t IT_EVENT_WRITE = 0x46;
|
||||
constexpr uint32_t IT_EVENT_WRITE_EOP = 0x47;
|
||||
constexpr uint32_t IT_EVENT_WRITE_EOS = 0x48;
|
||||
constexpr uint32_t IT_RELEASE_MEM = 0x49;
|
||||
constexpr uint32_t IT_DMA_DATA = 0x50;
|
||||
constexpr uint32_t IT_ACQUIRE_MEM = 0x58;
|
||||
constexpr uint32_t IT_REWIND = 0x59;
|
||||
constexpr uint32_t IT_SET_CONFIG_REG = 0x68;
|
||||
constexpr uint32_t IT_SET_CONTEXT_REG = 0x69;
|
||||
constexpr uint32_t IT_SET_SH_REG = 0x76;
|
||||
constexpr uint32_t IT_SET_QUEUE_REG = 0x78;
|
||||
constexpr uint32_t IT_SET_UCONFIG_REG = 0x79;
|
||||
constexpr uint32_t IT_WRITE_CONST_RAM = 0x81;
|
||||
constexpr uint32_t IT_DUMP_CONST_RAM = 0x83;
|
||||
constexpr uint32_t IT_INCREMENT_CE_COUNTER = 0x84;
|
||||
constexpr uint32_t IT_INCREMENT_DE_COUNTER = 0x85;
|
||||
constexpr uint32_t IT_WAIT_ON_CE_COUNTER = 0x86;
|
||||
constexpr uint32_t IT_WAIT_ON_DE_COUNTER_DIFF = 0x88;
|
||||
constexpr uint32_t IT_DISPATCH_DRAW_PREAMBLE = 0x8C;
|
||||
constexpr uint32_t IT_DISPATCH_DRAW = 0x8D;
|
||||
|
||||
constexpr uint32_t R_ZERO = 0x00;
|
||||
constexpr uint32_t R_VS = 0x01;
|
||||
constexpr uint32_t R_PS = 0x02;
|
||||
constexpr uint32_t R_DRAW_INDEX = 0x03;
|
||||
constexpr uint32_t R_DRAW_INDEX_AUTO = 0x04;
|
||||
constexpr uint32_t R_DRAW_RESET = 0x05;
|
||||
constexpr uint32_t R_WAIT_FLIP_DONE = 0x06;
|
||||
constexpr uint32_t R_CS = 0x07;
|
||||
constexpr uint32_t R_DISPATCH_DIRECT = 0x08;
|
||||
constexpr uint32_t R_DISPATCH_RESET = 0x09;
|
||||
constexpr uint32_t R_DISPATCH_WAIT_MEM = 0x0A;
|
||||
constexpr uint32_t R_PUSH_MARKER = 0x0B;
|
||||
constexpr uint32_t R_POP_MARKER = 0x0C;
|
||||
constexpr uint32_t R_VS_EMBEDDED = 0x0D;
|
||||
|
||||
constexpr uint32_t DB_RENDER_CONTROL = 0x0;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_DEPTH_CLEAR_ENABLE_SHIFT = 0;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_DEPTH_CLEAR_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_STENCIL_CLEAR_ENABLE_SHIFT = 1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_STENCIL_CLEAR_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_RESUMMARIZE_ENABLE_SHIFT = 4;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_RESUMMARIZE_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_STENCIL_COMPRESS_DISABLE_SHIFT = 5;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_STENCIL_COMPRESS_DISABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_DEPTH_COMPRESS_DISABLE_SHIFT = 6;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_DEPTH_COMPRESS_DISABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_CENTROID_SHIFT = 7;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_CENTROID_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_SAMPLE_SHIFT = 8;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_SAMPLE_MASK = 0xF;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_VIEW = 0x2;
|
||||
constexpr uint32_t DB_DEPTH_VIEW_SLICE_START_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_VIEW_SLICE_START_MASK = 0x7FF;
|
||||
constexpr uint32_t DB_DEPTH_VIEW_SLICE_MAX_SHIFT = 13;
|
||||
constexpr uint32_t DB_DEPTH_VIEW_SLICE_MAX_MASK = 0x7FF;
|
||||
|
||||
constexpr uint32_t DB_HTILE_DATA_BASE = 0x5;
|
||||
constexpr uint32_t DB_HTILE_DATA_BASE_BASE_256B_SHIFT = 0;
|
||||
constexpr uint32_t DB_HTILE_DATA_BASE_BASE_256B_MASK = 0xFFFFFFFF;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_CLEAR = 0xB;
|
||||
constexpr uint32_t DB_DEPTH_CLEAR_DEPTH_CLEAR_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_CLEAR_DEPTH_CLEAR_MASK = 0xFFFFFFFF;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_INFO = 0xF;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ADDR5_SWIZZLE_MASK_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ADDR5_SWIZZLE_MASK_MASK = 0xF;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ARRAY_MODE_SHIFT = 4;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ARRAY_MODE_MASK = 0xF;
|
||||
constexpr uint32_t DB_DEPTH_INFO_PIPE_CONFIG_SHIFT = 8;
|
||||
constexpr uint32_t DB_DEPTH_INFO_PIPE_CONFIG_MASK = 0x1F;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_WIDTH_SHIFT = 13;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_WIDTH_MASK = 0x3;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_HEIGHT_SHIFT = 15;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_HEIGHT_MASK = 0x3;
|
||||
constexpr uint32_t DB_DEPTH_INFO_MACRO_TILE_ASPECT_SHIFT = 17;
|
||||
constexpr uint32_t DB_DEPTH_INFO_MACRO_TILE_ASPECT_MASK = 0x3;
|
||||
constexpr uint32_t DB_DEPTH_INFO_NUM_BANKS_SHIFT = 19;
|
||||
constexpr uint32_t DB_DEPTH_INFO_NUM_BANKS_MASK = 0x3;
|
||||
|
||||
constexpr uint32_t DB_Z_INFO = 0x10;
|
||||
constexpr uint32_t DB_Z_INFO_FORMAT_SHIFT = 0;
|
||||
constexpr uint32_t DB_Z_INFO_FORMAT_MASK = 0x3;
|
||||
constexpr uint32_t DB_Z_INFO_NUM_SAMPLES_SHIFT = 2;
|
||||
constexpr uint32_t DB_Z_INFO_NUM_SAMPLES_MASK = 0x3;
|
||||
constexpr uint32_t DB_Z_INFO_TILE_MODE_INDEX_SHIFT = 20;
|
||||
constexpr uint32_t DB_Z_INFO_TILE_MODE_INDEX_MASK = 0x7;
|
||||
constexpr uint32_t DB_Z_INFO_TILE_SURFACE_ENABLE_SHIFT = 29;
|
||||
constexpr uint32_t DB_Z_INFO_TILE_SURFACE_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_Z_INFO_ZRANGE_PRECISION_SHIFT = 31;
|
||||
constexpr uint32_t DB_Z_INFO_ZRANGE_PRECISION_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t DB_STENCIL_INFO = 0x11;
|
||||
constexpr uint32_t DB_STENCIL_INFO_FORMAT_SHIFT = 0;
|
||||
constexpr uint32_t DB_STENCIL_INFO_FORMAT_MASK = 0x1;
|
||||
constexpr uint32_t DB_STENCIL_INFO_TILE_MODE_INDEX_SHIFT = 20;
|
||||
constexpr uint32_t DB_STENCIL_INFO_TILE_MODE_INDEX_MASK = 0x7;
|
||||
constexpr uint32_t DB_STENCIL_INFO_TILE_STENCIL_DISABLE_SHIFT = 29;
|
||||
constexpr uint32_t DB_STENCIL_INFO_TILE_STENCIL_DISABLE_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t DB_Z_READ_BASE = 0x12;
|
||||
constexpr uint32_t DB_Z_READ_BASE_BASE_256B_SHIFT = 0;
|
||||
constexpr uint32_t DB_Z_READ_BASE_BASE_256B_MASK = 0xFFFFFFFF;
|
||||
constexpr uint32_t DB_STENCIL_READ_BASE = 0x13;
|
||||
constexpr uint32_t DB_STENCIL_READ_BASE_BASE_256B_SHIFT = 0;
|
||||
constexpr uint32_t DB_STENCIL_READ_BASE_BASE_256B_MASK = 0xFFFFFFFF;
|
||||
constexpr uint32_t DB_Z_WRITE_BASE = 0x14;
|
||||
constexpr uint32_t DB_Z_WRITE_BASE_BASE_256B_SHIFT = 0;
|
||||
constexpr uint32_t DB_Z_WRITE_BASE_BASE_256B_MASK = 0xFFFFFFFF;
|
||||
constexpr uint32_t DB_STENCIL_WRITE_BASE = 0x15;
|
||||
constexpr uint32_t DB_STENCIL_WRITE_BASE_BASE_256B_SHIFT = 0;
|
||||
constexpr uint32_t DB_STENCIL_WRITE_BASE_BASE_256B_MASK = 0xFFFFFFFF;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_SIZE = 0x16;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_PITCH_TILE_MAX_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_PITCH_TILE_MAX_MASK = 0x7FF;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_HEIGHT_TILE_MAX_SHIFT = 11;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_HEIGHT_TILE_MAX_MASK = 0x7FF;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_SLICE = 0x17;
|
||||
constexpr uint32_t DB_DEPTH_SLICE_SLICE_TILE_MAX_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_SLICE_SLICE_TILE_MAX_MASK = 0x3FFFFF;
|
||||
|
||||
constexpr uint32_t PA_SC_VPORT_ZMIN_0 = 0xB4;
|
||||
|
||||
constexpr uint32_t PA_CL_VPORT_XSCALE = 0x10F;
|
||||
|
||||
constexpr uint32_t SPI_PS_INPUT_CNTL_0 = 0x191;
|
||||
|
||||
constexpr uint32_t CB_BLEND0_CONTROL = 0x1E0;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_COLOR_SRCBLEND_SHIFT = 0;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_COLOR_SRCBLEND_MASK = 0x1F;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_COLOR_COMB_FCN_SHIFT = 5;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_COLOR_COMB_FCN_MASK = 0x7;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_COLOR_DESTBLEND_SHIFT = 8;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_COLOR_DESTBLEND_MASK = 0x1F;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_ALPHA_SRCBLEND_SHIFT = 16;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_ALPHA_SRCBLEND_MASK = 0x1F;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_ALPHA_COMB_FCN_SHIFT = 21;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_ALPHA_COMB_FCN_MASK = 0x7;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_ALPHA_DESTBLEND_SHIFT = 24;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_ALPHA_DESTBLEND_MASK = 0x1F;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_SEPARATE_ALPHA_BLEND_SHIFT = 29;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_SEPARATE_ALPHA_BLEND_MASK = 0x1;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_ENABLE_SHIFT = 30;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_ENABLE_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_CONTROL = 0x200;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCIL_ENABLE_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCIL_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_Z_ENABLE_SHIFT = 1;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_Z_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_Z_WRITE_ENABLE_SHIFT = 2;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_Z_WRITE_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_DEPTH_BOUNDS_ENABLE_SHIFT = 3;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_DEPTH_BOUNDS_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_ZFUNC_SHIFT = 4;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_ZFUNC_MASK = 0x7;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_BACKFACE_ENABLE_SHIFT = 7;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_BACKFACE_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_SHIFT = 8;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_MASK = 0x7;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_BF_SHIFT = 20;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_BF_MASK = 0x7;
|
||||
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL = 0x205;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_CULL_FRONT_SHIFT = 0;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_CULL_FRONT_MASK = 0x1;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_CULL_BACK_SHIFT = 1;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_CULL_BACK_MASK = 0x1;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_FACE_SHIFT = 2;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_FACE_MASK = 0x1;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_POLY_MODE_SHIFT = 3;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_POLY_MODE_MASK = 0x3;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_POLYMODE_FRONT_PTYPE_SHIFT = 5;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_POLYMODE_FRONT_PTYPE_MASK = 0x7;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_POLYMODE_BACK_PTYPE_SHIFT = 8;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_POLYMODE_BACK_PTYPE_MASK = 0x7;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_POLY_OFFSET_FRONT_ENABLE_SHIFT = 11;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_POLY_OFFSET_FRONT_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_POLY_OFFSET_BACK_ENABLE_SHIFT = 12;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_POLY_OFFSET_BACK_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_VTX_WINDOW_OFFSET_ENABLE_SHIFT = 16;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_VTX_WINDOW_OFFSET_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_PROVOKING_VTX_LAST_SHIFT = 19;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_PROVOKING_VTX_LAST_MASK = 0x1;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_PERSP_CORR_DIS_SHIFT = 20;
|
||||
constexpr uint32_t PA_SU_SC_MODE_CNTL_PERSP_CORR_DIS_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t DB_HTILE_SURFACE = 0x2AF;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_LINEAR_SHIFT = 0;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_LINEAR_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_FULL_CACHE_SHIFT = 1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_FULL_CACHE_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_HTILE_USES_PRELOAD_WIN_SHIFT = 2;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_HTILE_USES_PRELOAD_WIN_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PRELOAD_SHIFT = 3;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PRELOAD_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_WIDTH_SHIFT = 4;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_WIDTH_MASK = 0x3F;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_HEIGHT_SHIFT = 10;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_HEIGHT_MASK = 0x3F;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_DST_OUTSIDE_ZERO_TO_ONE_SHIFT = 16;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_DST_OUTSIDE_ZERO_TO_ONE_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t VGT_SHADER_STAGES_EN = 0x2D5;
|
||||
|
||||
constexpr uint32_t CB_COLOR0_BASE = 0x318;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS = 0xA;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_SGPRS_MASK = 0xF;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS = 0xB;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SCRATCH_EN_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SCRATCH_EN_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_SHIFT = 1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_USER_SGPR_MASK = 0x1F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_WAVE_CNT_EN_SHIFT = 7;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_WAVE_CNT_EN_MASK = 0x0;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_PS_0 = 0xC;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_PS_15 = 0x1B;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_VS_0 = 0x4C;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_VS_15 = 0x5B;
|
||||
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1 = 0x212;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_BULKY_SHIFT = 24;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_BULKY_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2 = 0x213;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_SCRATCH_EN_SHIFT = 0;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_SCRATCH_EN_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_USER_SGPR_SHIFT = 1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_USER_SGPR_MASK = 0x1F;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_TGID_X_EN_SHIFT = 7;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_TGID_X_EN_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_TGID_Y_EN_SHIFT = 8;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_TGID_Y_EN_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_TGID_Z_EN_SHIFT = 9;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_TGID_Z_EN_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_TG_SIZE_EN_SHIFT = 10;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_TG_SIZE_EN_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_TIDIG_COMP_CNT_SHIFT = 11;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_TIDIG_COMP_CNT_MASK = 0x3;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_LDS_SIZE_SHIFT = 15;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_LDS_SIZE_MASK = 0x1FF;
|
||||
|
||||
constexpr uint32_t COMPUTE_USER_DATA_0 = 0x240;
|
||||
constexpr uint32_t COMPUTE_USER_DATA_15 = 0x24F;
|
||||
|
||||
void DumpPm4PacketStream(Core::File* file, uint32_t* cmd_buffer, uint32_t start_dw, uint32_t num_dw);
|
||||
|
||||
} // namespace Kyty::Libs::Graphics::Pm4
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_PM4_H_ */
|
||||
@@ -0,0 +1,554 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Vector.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct VertexShaderInfo;
|
||||
struct PixelShaderInfo;
|
||||
struct ComputeShaderInfo;
|
||||
|
||||
enum class ShaderType
|
||||
{
|
||||
Unknown,
|
||||
Vertex,
|
||||
Pixel,
|
||||
Fetch,
|
||||
Compute
|
||||
};
|
||||
|
||||
enum class ShaderInstructionType
|
||||
{
|
||||
Unknown,
|
||||
DsAppend,
|
||||
DsConsume,
|
||||
Exp,
|
||||
BufferLoadDword,
|
||||
BufferLoadFormatX,
|
||||
BufferLoadFormatXy,
|
||||
BufferLoadFormatXyz,
|
||||
BufferLoadFormatXyzw,
|
||||
BufferStoreDword,
|
||||
BufferStoreFormatX,
|
||||
ImageSample,
|
||||
TBufferLoadFormatXyzw,
|
||||
SAndn2B64,
|
||||
SAndSaveexecB64,
|
||||
SEndpgm,
|
||||
SCbranchExecz,
|
||||
SLshlB32,
|
||||
SLoadDwordx4,
|
||||
SLoadDwordx8,
|
||||
SBufferLoadDword,
|
||||
SBufferLoadDwordx4,
|
||||
SBufferLoadDwordx8,
|
||||
SBufferLoadDwordx16,
|
||||
SMovB32,
|
||||
SMovB64,
|
||||
SSetpcB64,
|
||||
SSwappcB64,
|
||||
SWaitcnt,
|
||||
SWqmB64,
|
||||
VAddI32,
|
||||
VAndB32,
|
||||
VCmpEqF32,
|
||||
VCmpEqU32,
|
||||
VCmpLeF32,
|
||||
VCmpLeU32,
|
||||
VCmpNeU32,
|
||||
VCmpNeqF32,
|
||||
VCmpxEqU32,
|
||||
VCmpxGtU32,
|
||||
VCndmaskB32,
|
||||
VCvtF32U32,
|
||||
VCvtPkrtzF16F32,
|
||||
VCvtU32F32,
|
||||
VMacF32,
|
||||
VMadakF32,
|
||||
VMadF32,
|
||||
VMaxF32,
|
||||
VMbcntHiU32B32,
|
||||
VMbcntLoU32B32,
|
||||
VMinF32,
|
||||
VMovB32,
|
||||
VMulF32,
|
||||
VInterpP1F32,
|
||||
VInterpP2F32,
|
||||
VRcpF32,
|
||||
VRsqF32,
|
||||
VSadU32,
|
||||
VSqrtF32,
|
||||
VSubF32,
|
||||
VSubI32,
|
||||
VSubrevF32,
|
||||
VSubrevI32,
|
||||
};
|
||||
|
||||
namespace ShaderInstructionFormat {
|
||||
|
||||
enum FormatByte : uint64_t
|
||||
{
|
||||
U = 0,
|
||||
N,
|
||||
D, // operand_to_str(inst.dst)
|
||||
D2, // operand_to_str(inst.dst2)
|
||||
S0, // operand_to_str(inst.src[0])
|
||||
S1, // operand_to_str(inst.src[1])
|
||||
S2, // operand_to_str(inst.src[2])
|
||||
S3, // operand_to_str(inst.src[3])
|
||||
DA2, // operand_array_to_str(inst.dst, 2)
|
||||
DA3, // operand_array_to_str(inst.dst, 3)
|
||||
DA4, // operand_array_to_str(inst.dst, 4)
|
||||
DA8, // operand_array_to_str(inst.dst, 8)
|
||||
DA16, // operand_array_to_str(inst.dst, 16)
|
||||
D2A2, // operand_array_to_str(inst.dst2, 2)
|
||||
D2A3, // operand_array_to_str(inst.dst2, 3)
|
||||
D2A4, // operand_array_to_str(inst.dst2, 4)
|
||||
S0A2, // operand_array_to_str(inst.src[0], 2)
|
||||
S0A3, // operand_array_to_str(inst.src[0], 3)
|
||||
S0A4, // operand_array_to_str(inst.src[0], 4)
|
||||
S1A2, // operand_array_to_str(inst.src[1], 2)
|
||||
S1A3, // operand_array_to_str(inst.src[1], 3)
|
||||
S1A4, // operand_array_to_str(inst.src[1], 4)
|
||||
S1A8, // operand_array_to_str(inst.src[1], 8)
|
||||
S2A2, // operand_array_to_str(inst.src[2], 2)
|
||||
S2A3, // operand_array_to_str(inst.src[2], 3)
|
||||
S2A4, // operand_array_to_str(inst.src[2], 4)
|
||||
Attr, // attr%u.%u <- inst.src[1].constant.u, inst.src[2].constant.u
|
||||
Idxen, // idxen
|
||||
Float4, // format:float4
|
||||
Pos0, // pos0
|
||||
Done, // done
|
||||
Param0, // param0
|
||||
Param1, // param1
|
||||
Param2, // param2
|
||||
Param3, // param3
|
||||
Mrt0, // mrt_color0
|
||||
Compr, // compr
|
||||
Vm, // vm
|
||||
L, // label_%u
|
||||
DmaskF, // dmask:0xf
|
||||
Dmask7, // dmask:0x7
|
||||
Gds, // gds
|
||||
};
|
||||
|
||||
constexpr uint64_t FormatDefine(std::initializer_list<uint64_t> f)
|
||||
{
|
||||
uint64_t r = 0;
|
||||
for (auto n: f)
|
||||
{
|
||||
r = (r << 8u) | n;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
enum Format : uint64_t
|
||||
{
|
||||
Unknown = FormatDefine({U}),
|
||||
Empty = FormatDefine({N}),
|
||||
Imm = FormatDefine({S0}),
|
||||
Label = FormatDefine({L}),
|
||||
Mrt0Vsrc0Vsrc1ComprVmDone = FormatDefine({Mrt0, S0, S1, Compr, Vm, Done}),
|
||||
Mrt0Vsrc0Vsrc1Vsrc2Vsrc3VmDone = FormatDefine({Mrt0, S0, S1, S2, S3, Vm, Done}),
|
||||
Param0Vsrc0Vsrc1Vsrc2Vsrc3 = FormatDefine({Param0, S0, S1, S2, S3}),
|
||||
Param1Vsrc0Vsrc1Vsrc2Vsrc3 = FormatDefine({Param1, S0, S1, S2, S3}),
|
||||
Param2Vsrc0Vsrc1Vsrc2Vsrc3 = FormatDefine({Param2, S0, S1, S2, S3}),
|
||||
Param3Vsrc0Vsrc1Vsrc2Vsrc3 = FormatDefine({Param3, S0, S1, S2, S3}),
|
||||
Pos0Vsrc0Vsrc1Vsrc2Vsrc3Done = FormatDefine({Pos0, S0, S1, S2, S3, Done}),
|
||||
Saddr = FormatDefine({S0A2}),
|
||||
Sdst4SbaseSoffset = FormatDefine({DA4, S0A2, S1}),
|
||||
Sdst8SbaseSoffset = FormatDefine({DA8, S0A2, S1}),
|
||||
SdstSvSoffset = FormatDefine({D, S0A4, S1}),
|
||||
Sdst4SvSoffset = FormatDefine({DA4, S0A4, S1}),
|
||||
Sdst8SvSoffset = FormatDefine({DA8, S0A4, S1}),
|
||||
Sdst16SvSoffset = FormatDefine({DA16, S0A4, S1}),
|
||||
SVdstSVsrc0 = FormatDefine({D, S0}),
|
||||
SVdstSVsrc0SVsrc1 = FormatDefine({D, S0, S1}),
|
||||
Sdst2Ssrc02 = FormatDefine({DA2, S0A2}),
|
||||
Sdst2Ssrc02Ssrc12 = FormatDefine({DA2, S0A2, S1A2}),
|
||||
SmaskVsrc0Vsrc1 = FormatDefine({DA2, S0, S1}),
|
||||
Vdata1VaddrSvSoffsIdxen = FormatDefine({D, S0, S1A4, S2, Idxen}),
|
||||
Vdata2VaddrSvSoffsIdxen = FormatDefine({DA2, S0, S1A4, S2, Idxen}),
|
||||
Vdata3VaddrSvSoffsIdxen = FormatDefine({DA3, S0, S1A4, S2, Idxen}),
|
||||
Vdata4VaddrSvSoffsIdxen = FormatDefine({DA4, S0, S1A4, S2, Idxen}),
|
||||
Vdata4VaddrSvSoffsIdxenFloat4 = FormatDefine({DA4, S0, S1A4, S2, Idxen, Float4}),
|
||||
Vdata3Vaddr3StSsDmask7 = FormatDefine({DA3, S0A3, S1A8, S2A4, Dmask7}),
|
||||
Vdata4Vaddr3StSsDmaskF = FormatDefine({DA4, S0A3, S1A8, S2A4, DmaskF}),
|
||||
VdstVsrc0Vsrc1Smask2 = FormatDefine({D, S0, S1, S2A2}),
|
||||
VdstVsrc0Vsrc1Vsrc2 = FormatDefine({D, S0, S1, S2}),
|
||||
VdstVsrcAttrChan = FormatDefine({D, S0, Attr}),
|
||||
VdstSdst2Vsrc0Vsrc1 = FormatDefine({D, D2A2, S0, S1}),
|
||||
VdstGds = FormatDefine({D, Gds})
|
||||
};
|
||||
|
||||
} // namespace ShaderInstructionFormat
|
||||
|
||||
enum class ShaderOperandType
|
||||
{
|
||||
Unknown,
|
||||
LiteralConstant,
|
||||
IntegerInlineConstant,
|
||||
FloatInlineConstant,
|
||||
VccLo,
|
||||
VccHi,
|
||||
ExecLo,
|
||||
ExecHi,
|
||||
ExecZ,
|
||||
Vgpr,
|
||||
Sgpr,
|
||||
M0
|
||||
};
|
||||
|
||||
union ShaderConstant
|
||||
{
|
||||
int32_t i;
|
||||
uint32_t u;
|
||||
float f;
|
||||
};
|
||||
|
||||
struct ShaderOperand
|
||||
{
|
||||
ShaderOperandType type = ShaderOperandType::Unknown;
|
||||
ShaderConstant constant = {0};
|
||||
int register_id = 0;
|
||||
int size = 0;
|
||||
float multiplier = 1.0f;
|
||||
bool negate = false;
|
||||
bool clamp = false;
|
||||
|
||||
bool operator==(const ShaderOperand& other) const
|
||||
{
|
||||
return type == other.type && constant.u == other.constant.u && register_id == other.register_id && size == other.size;
|
||||
}
|
||||
};
|
||||
|
||||
struct ShaderInstruction
|
||||
{
|
||||
uint32_t pc = 0;
|
||||
ShaderInstructionType type = ShaderInstructionType::Unknown;
|
||||
ShaderInstructionFormat::Format format = ShaderInstructionFormat::Unknown;
|
||||
ShaderOperand src[4];
|
||||
int src_num = 0;
|
||||
ShaderOperand dst;
|
||||
ShaderOperand dst2;
|
||||
};
|
||||
|
||||
class ShaderCode
|
||||
{
|
||||
public:
|
||||
ShaderCode() { m_instructions.Expand(128); };
|
||||
virtual ~ShaderCode() = default;
|
||||
KYTY_CLASS_DEFAULT_COPY(ShaderCode);
|
||||
|
||||
[[nodiscard]] const Vector<ShaderInstruction>& GetInstructions() const { return m_instructions; }
|
||||
Vector<ShaderInstruction>& GetInstructions() { return m_instructions; }
|
||||
[[nodiscard]] const Vector<uint32_t>& GetLabels() const { return m_labels; }
|
||||
Vector<uint32_t>& GetLabels() { return m_labels; }
|
||||
|
||||
[[nodiscard]] String DbgDump() const;
|
||||
|
||||
static String DbgInstructionToStr(const ShaderInstruction& inst);
|
||||
|
||||
[[nodiscard]] ShaderType GetType() const { return m_type; }
|
||||
void SetType(ShaderType type) { this->m_type = type; }
|
||||
|
||||
private:
|
||||
Vector<ShaderInstruction> m_instructions;
|
||||
Vector<uint32_t> m_labels;
|
||||
ShaderType m_type = ShaderType::Unknown;
|
||||
};
|
||||
|
||||
struct ShaderId
|
||||
{
|
||||
Vector<uint32_t> ids;
|
||||
|
||||
bool operator==(const ShaderId& other) const { return ids == other.ids; }
|
||||
bool operator!=(const ShaderId& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
struct ShaderBufferResource
|
||||
{
|
||||
uint32_t fields[4] = {0};
|
||||
|
||||
void UpdateAddress(uint64_t gpu_addr)
|
||||
{
|
||||
auto lo = static_cast<uint32_t>(gpu_addr & 0xffffffffu);
|
||||
auto hi = static_cast<uint32_t>(gpu_addr >> 32u);
|
||||
fields[0] = lo;
|
||||
fields[1] = (fields[1] & 0xfffff000u) | (hi & 0xfffu);
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t Base() const { return (fields[0] | (static_cast<uint64_t>(fields[1]) << 32u)) & 0xFFFFFFFFFFFu; }
|
||||
[[nodiscard]] uint16_t Stride() const { return (fields[1] >> 16u) & 0x3FFFu; }
|
||||
[[nodiscard]] bool SwizzleEnabled() const { return ((fields[1] >> 31u) & 0x1u) == 1; }
|
||||
[[nodiscard]] uint32_t NumRecords() const { return fields[2]; }
|
||||
[[nodiscard]] uint8_t DstSelX() const { return (fields[3] >> 0u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t DstSelY() const { return (fields[3] >> 3u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t DstSelZ() const { return (fields[3] >> 6u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t DstSelW() const { return (fields[3] >> 9u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t Nfmt() const { return (fields[3] >> 12u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t Dfmt() const { return (fields[3] >> 15u) & 0xFu; }
|
||||
[[nodiscard]] bool AddTid() const { return ((fields[3] >> 23u) & 0x1u) == 1; }
|
||||
|
||||
[[nodiscard]] uint8_t MemoryType() const
|
||||
{
|
||||
return ((fields[1] >> 7u) & 0x60u) | ((fields[3] >> 25u) & 0x1cu) | ((fields[1] >> 14u) & 0x3u);
|
||||
}
|
||||
};
|
||||
|
||||
struct ShaderTextureResource
|
||||
{
|
||||
uint32_t fields[8] = {0};
|
||||
|
||||
void UpdateAddress(uint64_t gpu_addr)
|
||||
{
|
||||
auto lo = static_cast<uint32_t>(gpu_addr & 0xffffffffu);
|
||||
auto hi = static_cast<uint32_t>(gpu_addr >> 32u);
|
||||
fields[0] = lo;
|
||||
fields[1] = (fields[1] & 0xffffffc0u) | (hi & 0x3fu);
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t Base() const { return ((fields[0] | (static_cast<uint64_t>(fields[1]) << 32u)) & 0x3FFFFFFFFFu) << 8u; }
|
||||
[[nodiscard]] uint16_t MinLod() const { return (fields[1] >> 8u) & 0xFFFu; }
|
||||
[[nodiscard]] uint8_t Dfmt() const { return (fields[1] >> 20u) & 0x3Fu; }
|
||||
[[nodiscard]] uint8_t Nfmt() const { return (fields[1] >> 26u) & 0xFu; }
|
||||
[[nodiscard]] uint16_t Width() const { return (fields[2] >> 0u) & 0x3FFFu; }
|
||||
[[nodiscard]] uint16_t Height() const { return (fields[2] >> 14u) & 0x3FFFu; }
|
||||
[[nodiscard]] uint8_t PerfMod() const { return (fields[2] >> 28u) & 0x7u; }
|
||||
[[nodiscard]] bool Interlaced() const { return ((fields[2] >> 31u) & 0x1u) == 1; }
|
||||
[[nodiscard]] uint8_t DstSelX() const { return (fields[3] >> 0u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t DstSelY() const { return (fields[3] >> 3u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t DstSelZ() const { return (fields[3] >> 6u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t DstSelW() const { return (fields[3] >> 9u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t BaseLevel() const { return (fields[3] >> 12u) & 0xFu; }
|
||||
[[nodiscard]] uint8_t LastLevel() const { return (fields[3] >> 16u) & 0xFu; }
|
||||
[[nodiscard]] uint8_t TilingIdx() const { return (fields[3] >> 20u) & 0x1Fu; }
|
||||
[[nodiscard]] bool Pow2Pad() const { return ((fields[3] >> 25u) & 0x1u) == 1; }
|
||||
[[nodiscard]] uint8_t Type() const { return (fields[3] >> 28u) & 0xFu; }
|
||||
|
||||
[[nodiscard]] uint16_t Depth() const { return (fields[4] >> 0u) & 0x1FFFu; }
|
||||
[[nodiscard]] uint16_t Pitch() const { return (fields[4] >> 13u) & 0x3FFFu; }
|
||||
[[nodiscard]] uint16_t BaseArray() const { return (fields[5] >> 0u) & 0x1FFFu; }
|
||||
[[nodiscard]] uint16_t LastArray() const { return (fields[5] >> 13u) & 0x1FFFu; }
|
||||
[[nodiscard]] uint16_t MinLodWarn() const { return (fields[6] >> 0u) & 0xFFFu; }
|
||||
[[nodiscard]] uint8_t CounterBankId() const { return (fields[6] >> 12u) & 0xFFu; }
|
||||
[[nodiscard]] bool LodHdwCntEn() const { return ((fields[6] >> 20u) & 0x1u) == 1; }
|
||||
|
||||
[[nodiscard]] uint8_t MemoryType() const
|
||||
{
|
||||
return ((fields[1] >> 6u) & 0x3u) | ((fields[1] >> 30u) << 2u) | ((fields[3] & 0x04000000u) == 0 ? 0x60u : 0x10u);
|
||||
}
|
||||
};
|
||||
|
||||
struct ShaderSamplerResource
|
||||
{
|
||||
uint32_t fields[4] = {0};
|
||||
|
||||
void UpdateIndex(uint32_t index) { fields[0] = index; }
|
||||
|
||||
[[nodiscard]] uint8_t ClampX() const { return (fields[0] >> 0u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t ClampY() const { return (fields[0] >> 3u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t ClampZ() const { return (fields[0] >> 6u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t MaxAnisoRatio() const { return (fields[0] >> 9u) & 0x7u; }
|
||||
[[nodiscard]] uint8_t DepthCompareFunc() const { return (fields[0] >> 12u) & 0x7u; }
|
||||
[[nodiscard]] bool ForceUnormCoords() const { return ((fields[0] >> 15u) & 0x1u) == 1; }
|
||||
[[nodiscard]] uint8_t AnisoThreshold() const { return (fields[0] >> 16u) & 0x7u; }
|
||||
[[nodiscard]] bool McCoordTrunc() const { return ((fields[0] >> 19u) & 0x1u) == 1; }
|
||||
[[nodiscard]] bool ForceDegamma() const { return ((fields[0] >> 20u) & 0x1u) == 1; }
|
||||
[[nodiscard]] uint8_t AnisoBias() const { return (fields[0] >> 21u) & 0x3Fu; }
|
||||
[[nodiscard]] bool TruncCoord() const { return ((fields[0] >> 27u) & 0x1u) == 1; }
|
||||
[[nodiscard]] bool DisableCubeWrap() const { return ((fields[0] >> 28u) & 0x1u) == 1; }
|
||||
[[nodiscard]] uint8_t FilterMode() const { return (fields[0] >> 29u) & 0x3u; }
|
||||
[[nodiscard]] uint16_t MinLod() const { return (fields[1] >> 0u) & 0xFFFu; }
|
||||
[[nodiscard]] uint16_t MaxLod() const { return (fields[1] >> 12u) & 0xFFFu; }
|
||||
[[nodiscard]] uint8_t PerfMip() const { return (fields[1] >> 24u) & 0xFu; }
|
||||
[[nodiscard]] uint8_t PerfZ() const { return (fields[1] >> 28u) & 0xFu; }
|
||||
[[nodiscard]] uint16_t LodBias() const { return (fields[2] >> 0u) & 0x3FFFu; }
|
||||
[[nodiscard]] uint8_t LodBiasSec() const { return (fields[2] >> 14u) & 0x3Fu; }
|
||||
[[nodiscard]] uint8_t XyMagFilter() const { return (fields[2] >> 20u) & 0x3u; }
|
||||
[[nodiscard]] uint8_t XyMinFilter() const { return (fields[2] >> 22u) & 0x3u; }
|
||||
[[nodiscard]] uint8_t ZFilter() const { return (fields[2] >> 24u) & 0x3u; }
|
||||
[[nodiscard]] uint8_t MipFilter() const { return (fields[2] >> 26u) & 0x3u; }
|
||||
[[nodiscard]] uint16_t BorderColorPtr() const { return (fields[3] >> 0u) & 0xFFFu; }
|
||||
[[nodiscard]] uint8_t BorderColorType() const { return (fields[3] >> 30u) & 0x3u; }
|
||||
};
|
||||
|
||||
struct ShaderGdsResource
|
||||
{
|
||||
uint32_t field = 0;
|
||||
|
||||
[[nodiscard]] uint16_t Base() const { return (field >> 16u) & 0xFFFFu; }
|
||||
[[nodiscard]] uint16_t Size() const { return field & 0xFFFFu; }
|
||||
};
|
||||
|
||||
struct ShaderExtendedResource
|
||||
{
|
||||
uint32_t fields[2] = {0};
|
||||
|
||||
void UpdateAddress(uint64_t gpu_addr)
|
||||
{
|
||||
auto lo = static_cast<uint32_t>(gpu_addr & 0xffffffffu);
|
||||
auto hi = static_cast<uint32_t>(gpu_addr >> 32u);
|
||||
fields[0] = lo;
|
||||
fields[1] = hi;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t Base() const { return (fields[0] | (static_cast<uint64_t>(fields[1]) << 32u)); }
|
||||
};
|
||||
|
||||
struct ShaderVertexInputBuffer
|
||||
{
|
||||
static constexpr int ATTR_MAX = 16;
|
||||
|
||||
uint64_t addr = 0;
|
||||
uint32_t stride = 0;
|
||||
uint32_t num_records = 0;
|
||||
int attr_num = 0;
|
||||
int attr_indices[ATTR_MAX] = {0};
|
||||
uint32_t attr_offsets[ATTR_MAX] = {0};
|
||||
};
|
||||
|
||||
struct ShaderVertexDestination
|
||||
{
|
||||
int register_start = 0;
|
||||
int registers_num = 0;
|
||||
};
|
||||
|
||||
enum class ShaderStorageUsage
|
||||
{
|
||||
Unknown,
|
||||
Constant,
|
||||
ReadOnly,
|
||||
ReadWrite,
|
||||
};
|
||||
|
||||
struct ShaderStorageResources
|
||||
{
|
||||
static constexpr int BUFFERS_MAX = 16;
|
||||
|
||||
ShaderBufferResource buffers[BUFFERS_MAX];
|
||||
ShaderStorageUsage usages[BUFFERS_MAX] = {};
|
||||
int slots[BUFFERS_MAX] = {0};
|
||||
int start_register[BUFFERS_MAX] = {0};
|
||||
bool extended[BUFFERS_MAX] = {};
|
||||
// int extended_index[BUFFERS_MAX] = {0};
|
||||
int buffers_num = 0;
|
||||
int binding_index = 0;
|
||||
};
|
||||
|
||||
struct ShaderTextureResources
|
||||
{
|
||||
static constexpr int RES_MAX = 16;
|
||||
|
||||
ShaderTextureResource textures[RES_MAX];
|
||||
int start_register[RES_MAX] = {0};
|
||||
bool extended[RES_MAX] = {};
|
||||
// int extended_index[RES_MAX] = {0};
|
||||
int textures_num = 0;
|
||||
int binding_index = 0;
|
||||
};
|
||||
|
||||
struct ShaderSamplerResources
|
||||
{
|
||||
static constexpr int RES_MAX = 16;
|
||||
|
||||
ShaderSamplerResource samplers[RES_MAX];
|
||||
int start_register[RES_MAX] = {0};
|
||||
bool extended[RES_MAX] = {};
|
||||
// int extended_index[RES_MAX] = {0};
|
||||
int samplers_num = 0;
|
||||
int binding_index = 0;
|
||||
};
|
||||
|
||||
struct ShaderGdsResources
|
||||
{
|
||||
static constexpr int POINTERS_MAX = 1;
|
||||
|
||||
ShaderGdsResource pointers[POINTERS_MAX];
|
||||
int slots[POINTERS_MAX] = {0};
|
||||
int start_register[POINTERS_MAX] = {0};
|
||||
bool extended[POINTERS_MAX] = {};
|
||||
// int extended_index[POINTERS_MAX] = {0};
|
||||
int pointers_num = 0;
|
||||
int binding_index = 0;
|
||||
};
|
||||
|
||||
struct ShaderExtendedResources
|
||||
{
|
||||
bool used = false;
|
||||
int slot = 0;
|
||||
// int dw_num = 0;
|
||||
int start_register = 0;
|
||||
ShaderExtendedResource data;
|
||||
};
|
||||
|
||||
struct ShaderResources
|
||||
{
|
||||
uint32_t push_constant_offset = 0;
|
||||
uint32_t push_constant_size = 0;
|
||||
uint32_t descriptor_set_slot = 0;
|
||||
ShaderStorageResources storage_buffers;
|
||||
ShaderTextureResources textures2D;
|
||||
ShaderSamplerResources samplers;
|
||||
ShaderGdsResources gds_pointers;
|
||||
ShaderExtendedResources extended;
|
||||
};
|
||||
|
||||
struct ShaderVertexInputInfo
|
||||
{
|
||||
static constexpr int RES_MAX = 16;
|
||||
|
||||
ShaderBufferResource resources[RES_MAX];
|
||||
ShaderVertexDestination resources_dst[RES_MAX];
|
||||
int resources_num = 0;
|
||||
bool fetch = false;
|
||||
ShaderVertexInputBuffer buffers[RES_MAX];
|
||||
int buffers_num = 0;
|
||||
int export_count = 0;
|
||||
ShaderResources bind;
|
||||
};
|
||||
|
||||
struct ShaderComputeInputInfo
|
||||
{
|
||||
uint32_t threads_num[3] = {};
|
||||
int workgroup_register = 0;
|
||||
ShaderResources bind;
|
||||
};
|
||||
|
||||
struct ShaderPixelInputInfo
|
||||
{
|
||||
uint32_t interpolator_settings[32] = {0};
|
||||
uint32_t input_num = 0;
|
||||
uint8_t target_output_mode[8] = {};
|
||||
bool ps_pos_xy = false;
|
||||
bool ps_pixel_kill_enable = false;
|
||||
ShaderResources bind;
|
||||
};
|
||||
|
||||
void ShaderGetInputInfoVS(const VertexShaderInfo* regs, ShaderVertexInputInfo* info);
|
||||
void ShaderGetInputInfoPS(const PixelShaderInfo* regs, const ShaderVertexInputInfo* vs_info, ShaderPixelInputInfo* ps_info);
|
||||
void ShaderGetInputInfoCS(const ComputeShaderInfo* regs, ShaderComputeInputInfo* info);
|
||||
void ShaderDbgDumpInputInfo(const ShaderVertexInputInfo* info);
|
||||
void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo* info);
|
||||
void ShaderDbgDumpInputInfo(const ShaderComputeInputInfo* info);
|
||||
ShaderId ShaderGetIdVS(const VertexShaderInfo* regs, const ShaderVertexInputInfo* input_info);
|
||||
ShaderId ShaderGetIdPS(const PixelShaderInfo* regs, const ShaderPixelInputInfo* input_info);
|
||||
ShaderId ShaderGetIdCS(const ComputeShaderInfo* regs, const ShaderComputeInputInfo* input_info);
|
||||
Vector<uint32_t> ShaderRecompileVS(const VertexShaderInfo* regs, const ShaderVertexInputInfo* input_info);
|
||||
Vector<uint32_t> ShaderRecompilePS(const PixelShaderInfo* regs, const ShaderPixelInputInfo* input_info);
|
||||
Vector<uint32_t> ShaderRecompileCS(const ComputeShaderInfo* regs, const ShaderComputeInputInfo* input_info);
|
||||
bool ShaderIsDisabled(uint64_t addr);
|
||||
void ShaderDisable(uint64_t id);
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_H_ */
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADERSPIRV_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADERSPIRV_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
class ShaderCode;
|
||||
struct ShaderVertexInputInfo;
|
||||
struct ShaderPixelInputInfo;
|
||||
struct ShaderComputeInputInfo;
|
||||
|
||||
String SpirvGenerateSource(const ShaderCode& code, const ShaderVertexInputInfo* vs_input_info, const ShaderPixelInputInfo* ps_input_info,
|
||||
const ShaderComputeInputInfo* cs_input_info);
|
||||
String SpirvGetEmbeddedVs(uint32_t id);
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADERSPIRV_H_ */
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_STORAGEBUFFER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_STORAGEBUFFER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
struct VulkanMemory;
|
||||
|
||||
class StorageBufferGpuObject: public GpuObject
|
||||
{
|
||||
public:
|
||||
StorageBufferGpuObject(uint64_t stride, uint64_t num_records, bool ronly)
|
||||
{
|
||||
params[0] = stride;
|
||||
params[1] = num_records;
|
||||
check_hash = true;
|
||||
read_only = ronly;
|
||||
type = Graphics::GpuMemoryObjectType::StorageBuffer;
|
||||
}
|
||||
|
||||
void* Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const override;
|
||||
bool Equal(const uint64_t* other) const override;
|
||||
|
||||
[[nodiscard]] write_back_func_t GetWriteBackFunc() const override;
|
||||
[[nodiscard]] delete_func_t GetDeleteFunc() const override;
|
||||
[[nodiscard]] update_func_t GetUpdateFunc() const override;
|
||||
};
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_STORAGEBUFFER_H_ */
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_TEXTURE_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_TEXTURE_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
struct VulkanMemory;
|
||||
|
||||
class TextureObject: public GpuObject
|
||||
{
|
||||
public:
|
||||
static constexpr int PARAM_DFMT = 0;
|
||||
static constexpr int PARAM_NFMT = 1;
|
||||
static constexpr int PARAM_WIDTH = 2;
|
||||
static constexpr int PARAM_HEIGHT = 3;
|
||||
static constexpr int PARAM_LEVELS = 4;
|
||||
static constexpr int PARAM_TILE = 5;
|
||||
static constexpr int PARAM_NEO = 6;
|
||||
static constexpr int PARAM_SWIZZLE = 7;
|
||||
|
||||
TextureObject(uint32_t dfmt, uint32_t nfmt, uint32_t width, uint32_t height, uint32_t levels, bool htile, bool neo, uint32_t swizzle)
|
||||
{
|
||||
params[PARAM_DFMT] = dfmt;
|
||||
params[PARAM_NFMT] = nfmt;
|
||||
params[PARAM_WIDTH] = width;
|
||||
params[PARAM_HEIGHT] = height;
|
||||
params[PARAM_LEVELS] = levels;
|
||||
params[PARAM_TILE] = htile ? 1 : 0;
|
||||
params[PARAM_NEO] = neo ? 1 : 0;
|
||||
params[PARAM_SWIZZLE] = swizzle;
|
||||
check_hash = true;
|
||||
type = Graphics::GpuMemoryObjectType::Texture;
|
||||
}
|
||||
|
||||
void* Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const override;
|
||||
bool Equal(const uint64_t* other) const override;
|
||||
|
||||
[[nodiscard]] write_back_func_t GetWriteBackFunc() const override { return nullptr; };
|
||||
[[nodiscard]] delete_func_t GetDeleteFunc() const override;
|
||||
[[nodiscard]] update_func_t GetUpdateFunc() const override;
|
||||
};
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_TEXTURE_H_ */
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_TILE_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_TILE_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
enum class TileMode
|
||||
{
|
||||
VideoOutLinear,
|
||||
VideoOutTiled,
|
||||
TextureLinear,
|
||||
TextureTiled,
|
||||
};
|
||||
|
||||
void TileInit();
|
||||
void TileConvertTiledToLinear(void* dst, const void* src, TileMode mode, uint32_t width, uint32_t height, bool 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);
|
||||
|
||||
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);
|
||||
void TileGetVideoOutSize(uint32_t width, uint32_t height, bool tile, bool neo, uint32_t* size);
|
||||
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);
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_TILE_H_ */
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_UTILS_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_UTILS_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty {
|
||||
template <typename T>
|
||||
class Vector;
|
||||
} // namespace Kyty
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
class CommandBuffer;
|
||||
struct GraphicContext;
|
||||
struct VulkanBuffer;
|
||||
struct VideoOutVulkanImage;
|
||||
struct TextureVulkanImage;
|
||||
struct DepthStencilVulkanImage;
|
||||
struct VulkanSwapchain;
|
||||
|
||||
struct BufferImageCopy
|
||||
{
|
||||
uint32_t offset;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
};
|
||||
|
||||
void UtilBufferToImage(CommandBuffer* buffer, VulkanBuffer* src_buffer, VideoOutVulkanImage* dst_image);
|
||||
void UtilBufferToImage(CommandBuffer* buffer, VulkanBuffer* src_buffer, TextureVulkanImage* dst_image,
|
||||
const Vector<BufferImageCopy>& regions);
|
||||
void UtilBlitImage(CommandBuffer* buffer, VideoOutVulkanImage* src_image, VulkanSwapchain* dst_swapchain);
|
||||
void UtilFillImage(GraphicContext* ctx, VideoOutVulkanImage* dst_image, const void* src_data, uint64_t size);
|
||||
void UtilFillImage(GraphicContext* ctx, TextureVulkanImage* dst_image, const void* src_data, uint64_t size,
|
||||
const Vector<BufferImageCopy>& regions);
|
||||
void UtilCopyBuffer(VulkanBuffer* src_buffer, VulkanBuffer* dst_buffer, uint64_t size);
|
||||
void UtilSetImageLayoutOptimal(DepthStencilVulkanImage* image);
|
||||
void UtilSetImageLayoutOptimal(VideoOutVulkanImage* image);
|
||||
|
||||
void VulkanCreateBuffer(GraphicContext* gctx, uint64_t size, VulkanBuffer* buffer);
|
||||
void VulkanDeleteBuffer(GraphicContext* gctx, VulkanBuffer* buffer);
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_UTILS_H_ */
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_VERTEXBUFFER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_VERTEXBUFFER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
struct VulkanMemory;
|
||||
|
||||
class VertexBufferGpuObject: public GpuObject
|
||||
{
|
||||
public:
|
||||
VertexBufferGpuObject()
|
||||
{
|
||||
check_hash = true;
|
||||
type = Graphics::GpuMemoryObjectType::VertexBuffer;
|
||||
}
|
||||
|
||||
void* Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const override;
|
||||
bool Equal(const uint64_t* other) const override;
|
||||
|
||||
[[nodiscard]] write_back_func_t GetWriteBackFunc() const override { return nullptr; };
|
||||
[[nodiscard]] delete_func_t GetDeleteFunc() const override;
|
||||
[[nodiscard]] update_func_t GetUpdateFunc() const override;
|
||||
};
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_VERTEXBUFFER_H_ */
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_VIDEOOUT_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_VIDEOOUT_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
//#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Kernel/EventQueue.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
// struct VulkanSwapchain;
|
||||
struct VideoOutVulkanImage;
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
namespace Kyty::Libs::VideoOut {
|
||||
|
||||
struct VideoOutResolutionStatus;
|
||||
struct VideoOutBufferAttribute;
|
||||
struct VideoOutFlipStatus;
|
||||
|
||||
struct VideoOutBufferImageInfo
|
||||
{
|
||||
Graphics::VideoOutVulkanImage* image = nullptr;
|
||||
uint32_t index = static_cast<uint32_t>(-1);
|
||||
uint64_t buffer_size = 0;
|
||||
};
|
||||
|
||||
void VideoOutInit(uint32_t width, uint32_t height);
|
||||
VideoOutBufferImageInfo VideoOutGetImage(uint64_t addr);
|
||||
void VideoOutWaitFlipDone(int handle, int index);
|
||||
|
||||
KYTY_SYSV_ABI int VideoOutOpen(int user_id, int bus_type, int index, const void* param);
|
||||
KYTY_SYSV_ABI int VideoOutClose(int handle);
|
||||
KYTY_SYSV_ABI int VideoOutGetResolutionStatus(int handle, VideoOutResolutionStatus* status);
|
||||
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);
|
||||
KYTY_SYSV_ABI int VideoOutSetFlipRate(int handle, int rate);
|
||||
KYTY_SYSV_ABI int VideoOutAddFlipEvent(LibKernel::EventQueue::KernelEqueue eq, int handle, void* udata);
|
||||
KYTY_SYSV_ABI int VideoOutRegisterBuffers(int handle, int start_index, void* const* addresses, int buffer_num,
|
||||
const VideoOutBufferAttribute* attribute);
|
||||
KYTY_SYSV_ABI int VideoOutSubmitFlip(int handle, int index, int flip_mode, int64_t flip_arg);
|
||||
KYTY_SYSV_ABI int VideoOutGetFlipStatus(int handle, VideoOutFlipStatus* status);
|
||||
|
||||
bool FlipWindow(uint32_t micros);
|
||||
|
||||
} // namespace Kyty::Libs::VideoOut
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_VIDEOOUT_H_ */
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_VIDEOOUTBUFFER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_VIDEOOUTBUFFER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
struct VulkanMemory;
|
||||
|
||||
class VideoOutBufferObject: public GpuObject
|
||||
{
|
||||
public:
|
||||
static constexpr int PARAM_FORMAT = 0;
|
||||
static constexpr int PARAM_WIDTH = 1;
|
||||
static constexpr int PARAM_HEIGHT = 2;
|
||||
static constexpr int PARAM_TILED = 3;
|
||||
static constexpr int PARAM_NEO = 4;
|
||||
|
||||
explicit VideoOutBufferObject(uint32_t pixel_format, uint32_t width, uint32_t height, bool tiled, bool neo)
|
||||
{
|
||||
params[PARAM_FORMAT] = pixel_format;
|
||||
params[PARAM_WIDTH] = width;
|
||||
params[PARAM_HEIGHT] = height;
|
||||
params[PARAM_TILED] = tiled ? 1 : 0;
|
||||
params[PARAM_NEO] = neo ? 1 : 0;
|
||||
check_hash = true;
|
||||
type = Graphics::GpuMemoryObjectType::VideoOutBuffer;
|
||||
}
|
||||
|
||||
void* Create(GraphicContext* ctx, const uint64_t* vaddr, const uint64_t* size, int vaddr_num, VulkanMemory* mem) const override;
|
||||
bool Equal(const uint64_t* other) const override;
|
||||
|
||||
[[nodiscard]] write_back_func_t GetWriteBackFunc() const override { return nullptr; };
|
||||
[[nodiscard]] delete_func_t GetDeleteFunc() const override;
|
||||
[[nodiscard]] update_func_t GetUpdateFunc() const override;
|
||||
};
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_VIDEOOUTBUFFER_H_ */
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_WINDOW_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_WINDOW_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
struct VkSurfaceCapabilitiesKHR;
|
||||
|
||||
namespace Kyty::Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
struct VideoOutVulkanImage;
|
||||
|
||||
VkSurfaceCapabilitiesKHR* VulkanGetSurfaceCapabilities();
|
||||
|
||||
GraphicContext* WindowGetGraphicContext();
|
||||
|
||||
void WindowInit(uint32_t width, uint32_t height);
|
||||
void WindowRun();
|
||||
void WindowWaitForGraphicInitialized();
|
||||
void WindowDrawBuffer(VideoOutVulkanImage* image);
|
||||
|
||||
} // namespace Kyty::Libs::Graphics
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_WINDOW_H_ */
|
||||
@@ -0,0 +1,150 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_JIT_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_JIT_H_
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Loader::Jit {
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct JmpWithIndex
|
||||
{
|
||||
void SetIndex(uint32_t index) { *reinterpret_cast<uint32_t*>(&code[1]) = index; }
|
||||
|
||||
void SetFunc(void* handler)
|
||||
{
|
||||
auto func_addr = reinterpret_cast<int64_t>(handler);
|
||||
auto rip_addr = reinterpret_cast<int64_t>(&code[10]);
|
||||
auto offset64 = func_addr - rip_addr;
|
||||
auto offset32 = static_cast<uint32_t>(static_cast<uint64_t>(offset64) & 0xffffffffu);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(&code[6]) = offset32;
|
||||
}
|
||||
|
||||
static uint64_t GetSize() { return 16; }
|
||||
|
||||
// 68 00 00 00 00 push <index>
|
||||
// E9 E0 FF FF FF jmp <handler>
|
||||
uint8_t code[16] = {0x68, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
|
||||
};
|
||||
|
||||
struct CallPlt
|
||||
{
|
||||
explicit CallPlt(uint32_t table_size)
|
||||
{
|
||||
for (uint32_t index = 0; index < table_size; index++)
|
||||
{
|
||||
auto* c = new (&code[32] + JmpWithIndex::GetSize() * index) JmpWithIndex;
|
||||
c->SetIndex(index);
|
||||
c->SetFunc(this);
|
||||
}
|
||||
}
|
||||
|
||||
void SetPltGot(uint64_t vaddr) { *reinterpret_cast<uint64_t*>(&code[2]) = vaddr; }
|
||||
|
||||
uint64_t GetAddr(uint32_t index) { return reinterpret_cast<uint64_t>(&code[32] + JmpWithIndex::GetSize() * index); }
|
||||
|
||||
static uint64_t GetSize(uint32_t table_size) { return 32 + JmpWithIndex::GetSize() * table_size; }
|
||||
|
||||
// 0: 49 bb 88 77 66 55 44 movabs r11,0x1122334455667788
|
||||
// 7: 33 22 11
|
||||
// a: 41 ff 73 08 push QWORD PTR [r11+0x8]
|
||||
// e: 41 ff 63 10 jmp QWORD PTR [r11+0x10]
|
||||
uint8_t code[32] = {0x49, 0xBB, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x41, 0xFF,
|
||||
0x73, 0x08, 0x41, 0xFF, 0x63, 0x10, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
|
||||
};
|
||||
|
||||
struct JmpRax
|
||||
{
|
||||
template <class Handler>
|
||||
void SetFunc(Handler func)
|
||||
{
|
||||
*reinterpret_cast<Handler*>(&code[2]) = func;
|
||||
}
|
||||
|
||||
// mov rax, 0x1122334455667788
|
||||
// jmp rax
|
||||
uint8_t code[16] = {0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xFF, 0xE0};
|
||||
};
|
||||
|
||||
struct Call9
|
||||
{
|
||||
template <class Handler>
|
||||
void SetFunc(Handler func)
|
||||
{
|
||||
auto func_addr = reinterpret_cast<int64_t>(reinterpret_cast<void*>(func));
|
||||
auto rip_addr = reinterpret_cast<int64_t>(&code[5]);
|
||||
auto offset64 = func_addr - rip_addr;
|
||||
auto offset32 = static_cast<uint32_t>(static_cast<uint64_t>(offset64) & 0xffffffffu);
|
||||
|
||||
*reinterpret_cast<uint32_t*>(&code[1]) = offset32;
|
||||
}
|
||||
|
||||
static uint64_t GetSize() { return 9; }
|
||||
|
||||
// call func
|
||||
// mov rax,rax
|
||||
// nop
|
||||
uint8_t code[9] = {0xE8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0xC0, 0x90};
|
||||
};
|
||||
|
||||
struct SafeCall
|
||||
{
|
||||
using func_t = KYTY_MS_ABI uint8_t* (*)();
|
||||
|
||||
void SetFunc(func_t func) { *reinterpret_cast<func_t*>(&code[0x22]) = func; }
|
||||
void SetRegSaveArea(uint8_t* area) { *reinterpret_cast<uint8_t**>(&code[0x18]) = area; }
|
||||
void SetLockVar(uint8_t* lock_var) { *reinterpret_cast<uint8_t**>(&code[0x0e]) = lock_var; }
|
||||
|
||||
static uint64_t GetSize() { return 0x1000; }
|
||||
|
||||
uint8_t code[0x6c] = {
|
||||
/*00*/ 0x51, // push rcx /* Save general purpose registers */
|
||||
/*01*/ 0x52, // push rdx
|
||||
/*02*/ 0x41, 0x50, // push r8
|
||||
/*04*/ 0x41, 0x51, // push r9
|
||||
/*06*/ 0x41, 0x52, // push r10
|
||||
/*08*/ 0x41, 0x53, // push r11
|
||||
/*0a*/ 0x57, // push rdi
|
||||
/*0b*/ 0x56, // push rsi
|
||||
/*0c*/ 0x48, 0xbf, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // movabs rdi,0x1122334455667788
|
||||
/*16*/ 0x48, 0xbe, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // movabs rsi,0x1122334455667788
|
||||
/*20*/ 0x48, 0xb9, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // movabs rcx,0x1122334455667788
|
||||
/*2a*/ 0xb0, 0x01, // mov al,0x1 /* Lock */
|
||||
/*2c*/ 0x86, 0x07, // xchg BYTE PTR [rdi],al
|
||||
/*2e*/ 0x84, 0xc0, // test al,al
|
||||
/*30*/ 0x75, 0xf8, // jne 2a <LOCK>
|
||||
/*32*/ 0xb8, 0xff, 0xff, 0xff, 0xff, // mov eax,0xffffffff
|
||||
/*37*/ 0xba, 0xff, 0xff, 0xff, 0xff, // mov edx,0xffffffff
|
||||
/*3c*/ 0x0f, 0xae, 0x26, // xsave [rsi] /* Save float registers */
|
||||
/*3f*/ 0x48, 0x83, 0xec, 0x08, // sub rsp,0x8 /* Align stack */
|
||||
/*43*/ 0xff, 0xd1, // call rcx
|
||||
/*45*/ 0x48, 0x83, 0xc4, 0x08, // add rsp,0x8
|
||||
/*49*/ 0x48, 0x89, 0xc1, // mov rcx,rax
|
||||
/*4c*/ 0xb8, 0xff, 0xff, 0xff, 0xff, // mov eax,0xffffffff
|
||||
/*51*/ 0xba, 0xff, 0xff, 0xff, 0xff, // mov edx,0xffffffff
|
||||
/*56*/ 0x0f, 0xae, 0x2e, // xrstor [rsi] /* Restore float registers */
|
||||
/*59*/ 0x48, 0x89, 0xc8, // mov rax,rcx
|
||||
/*5c*/ 0xc6, 0x07, 0x00, // mov BYTE PTR [rdi],0x0 /* Unlock */
|
||||
/*5f*/ 0x5e, // pop rsi /* Restore general purpose registers */
|
||||
/*60*/ 0x5f, // pop rdi
|
||||
/*61*/ 0x41, 0x5b, // pop r11
|
||||
/*63*/ 0x41, 0x5a, // pop r10
|
||||
/*65*/ 0x41, 0x59, // pop r9
|
||||
/*67*/ 0x41, 0x58, // pop r8
|
||||
/*69*/ 0x5a, // pop rdx
|
||||
/*6a*/ 0x59, // pop rcx
|
||||
/*6b*/ 0xc3, // ret
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
} // namespace Kyty::Loader::Jit
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_JIT_H_ */
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTFLAG_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTFLAG_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Kernel/Pthread.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::LibKernel::EventFlag {
|
||||
|
||||
class KernelEventFlagPrivate;
|
||||
|
||||
using KernelEventFlag = KernelEventFlagPrivate*;
|
||||
|
||||
int KYTY_SYSV_ABI KernelCreateEventFlag(KernelEventFlag* ef, const char* name, uint32_t attr, uint64_t init_pattern, const void* param);
|
||||
int KYTY_SYSV_ABI KernelDeleteEventFlag(KernelEventFlag ef);
|
||||
int KYTY_SYSV_ABI KernelWaitEventFlag(KernelEventFlag ef, uint64_t bit_pattern, uint32_t wait_mode, uint64_t* result_pat,
|
||||
KernelUseconds* timeout);
|
||||
int KYTY_SYSV_ABI KernelPollEventFlag(KernelEventFlag ef, uint64_t bit_pattern, uint32_t wait_mode, uint64_t* result_pat);
|
||||
int KYTY_SYSV_ABI KernelSetEventFlag(KernelEventFlag ef, uint64_t bit_pattern);
|
||||
int KYTY_SYSV_ABI KernelClearEventFlag(KernelEventFlag ef, uint64_t bit_pattern);
|
||||
int KYTY_SYSV_ABI KernelCancelEventFlag(KernelEventFlag ef, uint64_t set_pattern, int* num_wait_threads);
|
||||
|
||||
} // namespace Kyty::Libs::LibKernel::EventFlag
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTFLAG_H_ */
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTQUEUE_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTQUEUE_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Kernel/Pthread.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::LibKernel::EventQueue {
|
||||
|
||||
constexpr int16_t KERNEL_EVFILT_TIMER = -7;
|
||||
constexpr int16_t KERNEL_EVFILT_READ = -1;
|
||||
constexpr int16_t KERNEL_EVFILT_WRITE = -2;
|
||||
constexpr int16_t KERNEL_EVFILT_USER = -11;
|
||||
constexpr int16_t KERNEL_EVFILT_FILE = -4;
|
||||
constexpr int16_t KERNEL_EVFILT_GRAPHICS = -14;
|
||||
constexpr int16_t KERNEL_EVFILT_VIDEO_OUT = -13;
|
||||
constexpr int16_t KERNEL_EVFILT_HRTIMER = -15;
|
||||
|
||||
class KernelEqueuePrivate;
|
||||
struct KernelEqueueEvent;
|
||||
|
||||
using trigger_func_t = void (*)(KernelEqueueEvent* event, void* trigger_data);
|
||||
using reset_func_t = void (*)(KernelEqueueEvent* event);
|
||||
using delete_func_t = void (*)(KernelEqueueEvent* event);
|
||||
|
||||
struct KernelEvent
|
||||
{
|
||||
uintptr_t ident = 0;
|
||||
int16_t filter = 0;
|
||||
uint16_t flags = 0;
|
||||
uint32_t fflags = 0;
|
||||
intptr_t data = 0;
|
||||
void* udata = nullptr;
|
||||
};
|
||||
|
||||
struct KernelFilter
|
||||
{
|
||||
void* data = nullptr;
|
||||
trigger_func_t trigger_func = nullptr;
|
||||
reset_func_t reset_func = nullptr;
|
||||
delete_func_t delete_func = nullptr;
|
||||
};
|
||||
|
||||
struct KernelEqueueEvent
|
||||
{
|
||||
bool triggered = false;
|
||||
KernelEvent event;
|
||||
KernelFilter filter;
|
||||
};
|
||||
|
||||
using KernelEqueue = KernelEqueuePrivate*;
|
||||
|
||||
int KYTY_SYSV_ABI KernelAddEvent(KernelEqueue eq, const KernelEqueueEvent& event);
|
||||
int KYTY_SYSV_ABI KernelTriggerEvent(KernelEqueue eq, uintptr_t ident, int16_t filter, void* trigger_data);
|
||||
int KYTY_SYSV_ABI KernelDeleteEvent(KernelEqueue eq, uintptr_t ident, int16_t filter);
|
||||
|
||||
int KYTY_SYSV_ABI KernelCreateEqueue(KernelEqueue* eq, const char* name);
|
||||
int KYTY_SYSV_ABI KernelDeleteEqueue(KernelEqueue eq);
|
||||
int KYTY_SYSV_ABI KernelWaitEqueue(KernelEqueue eq, KernelEvent* ev, int num, int* out, const KernelUseconds* timo);
|
||||
|
||||
intptr_t KYTY_SYSV_ABI KernelGetEventData(const KernelEvent* ev);
|
||||
intptr_t KYTY_SYSV_ABI KernelGetEventFflags(const KernelEvent* ev);
|
||||
int KYTY_SYSV_ABI KernelGetEventFilter(const KernelEvent* ev);
|
||||
uintptr_t KYTY_SYSV_ABI KernelGetEventId(const KernelEvent* ev);
|
||||
void* KYTY_SYSV_ABI KernelGetEventUserData(const KernelEvent* ev);
|
||||
int KYTY_SYSV_ABI KernelGetEventError(const KernelEvent* ev);
|
||||
|
||||
} // namespace Kyty::Libs::LibKernel::EventQueue
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTQUEUE_H_ */
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_FILESYSTEM_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_FILESYSTEM_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Kernel/Pthread.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::LibKernel::FileSystem {
|
||||
|
||||
struct FileStat
|
||||
{
|
||||
uint32_t st_dev;
|
||||
uint32_t st_ino;
|
||||
uint16_t st_mode;
|
||||
uint16_t st_nlink;
|
||||
uint32_t st_uid;
|
||||
uint32_t st_gid;
|
||||
uint32_t st_rdev;
|
||||
KernelTimespec st_atim;
|
||||
KernelTimespec st_mtim;
|
||||
KernelTimespec st_ctim;
|
||||
int64_t st_size;
|
||||
int64_t st_blocks;
|
||||
uint32_t st_blksize;
|
||||
uint32_t st_flags;
|
||||
uint32_t st_gen;
|
||||
int32_t st_lspare;
|
||||
KernelTimespec st_birthtim;
|
||||
unsigned int: (8 / 2) * (16 - static_cast<int>(sizeof(KernelTimespec)));
|
||||
unsigned int: (8 / 2) * (16 - static_cast<int>(sizeof(KernelTimespec)));
|
||||
};
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(FileSystem);
|
||||
|
||||
void Mount(const String& folder, const String& point);
|
||||
void Umount(const String& folder_or_point);
|
||||
String GetRealFilename(const String& mounted_file_name);
|
||||
|
||||
int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode);
|
||||
int KYTY_SYSV_ABI KernelClose(int d);
|
||||
int64_t KYTY_SYSV_ABI KernelRead(int d, void* buf, size_t nbytes);
|
||||
int64_t KYTY_SYSV_ABI KernelPread(int d, void* buf, size_t nbytes, int64_t offset);
|
||||
int64_t KYTY_SYSV_ABI KernelWrite(int d, const void* buf, size_t nbytes);
|
||||
int64_t KYTY_SYSV_ABI KernelPwrite(int d, const void* buf, size_t nbytes, int64_t offset);
|
||||
int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence);
|
||||
int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb);
|
||||
int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb);
|
||||
int KYTY_SYSV_ABI KernelUnlink(const char* path);
|
||||
int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* basep);
|
||||
|
||||
} // namespace Kyty::Libs::LibKernel::FileSystem
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_FILESYSTEM_H_ */
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_MEMORY_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_MEMORY_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::LibKernel::Memory {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Memory);
|
||||
|
||||
int KYTY_SYSV_ABI KernelMapNamedFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags, const char* name);
|
||||
int KYTY_SYSV_ABI KernelMunmap(uint64_t vaddr, size_t len);
|
||||
size_t KYTY_SYSV_ABI KernelGetDirectMemorySize();
|
||||
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);
|
||||
int KYTY_SYSV_ABI KernelReleaseDirectMemory(int64_t start, size_t len);
|
||||
int KYTY_SYSV_ABI KernelMapDirectMemory(void** addr, size_t len, int prot, int flags, int64_t direct_memory_start, size_t alignment);
|
||||
int KYTY_SYSV_ABI KernelQueryMemoryProtection(void* addr, void** start, void** end, int* prot);
|
||||
|
||||
} // namespace Kyty::Libs::LibKernel::Memory
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_MEMORY_H_ */
|
||||
@@ -0,0 +1,159 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_PTHREAD_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_PTHREAD_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
// IWYU pragma: no_include <pthread.h>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
extern "C" {
|
||||
struct sched_param;
|
||||
}
|
||||
|
||||
namespace Kyty::Loader {
|
||||
struct Program;
|
||||
} // namespace Kyty::Loader
|
||||
|
||||
namespace Kyty::Libs::LibKernel {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Pthread);
|
||||
|
||||
struct PthreadAttrPrivate;
|
||||
struct PthreadPrivate;
|
||||
struct PthreadMutexPrivate;
|
||||
struct PthreadMutexattrPrivate;
|
||||
struct PthreadRwlockPrivate;
|
||||
struct PthreadRwlockattrPrivate;
|
||||
struct PthreadCondattrPrivate;
|
||||
struct PthreadCondPrivate;
|
||||
|
||||
struct KernelTimespec
|
||||
{
|
||||
int64_t tv_sec;
|
||||
int64_t tv_nsec;
|
||||
};
|
||||
|
||||
struct KernelTimeval
|
||||
{
|
||||
int64_t tv_sec;
|
||||
int64_t tv_usec;
|
||||
};
|
||||
|
||||
using PthreadAttr = PthreadAttrPrivate*;
|
||||
using Pthread = PthreadPrivate*;
|
||||
using KernelCpumask = uint64_t;
|
||||
using PthreadMutex = PthreadMutexPrivate*;
|
||||
using PthreadMutexattr = PthreadMutexattrPrivate*;
|
||||
using KernelSchedParam = struct sched_param;
|
||||
using PthreadRwlock = PthreadRwlockPrivate*;
|
||||
using PthreadRwlockattr = PthreadRwlockattrPrivate*;
|
||||
using KernelUseconds = unsigned int;
|
||||
using PthreadCondattr = PthreadCondattrPrivate*;
|
||||
using PthreadCond = PthreadCondPrivate*;
|
||||
using KernelClockid = int32_t;
|
||||
|
||||
using pthread_entry_func_t = KYTY_SYSV_ABI void* (*)(void*);
|
||||
using thread_dtors_func_t = KYTY_SYSV_ABI void (*)();
|
||||
|
||||
void PthreadInitSelfForMainThread();
|
||||
void PthreadDeleteStaticObjects(Loader::Program* program);
|
||||
|
||||
int KYTY_SYSV_ABI PthreadMutexattrInit(PthreadMutexattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadMutexattrDestroy(PthreadMutexattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadMutexattrSettype(PthreadMutexattr* attr, int type);
|
||||
int KYTY_SYSV_ABI PthreadMutexattrSetprotocol(PthreadMutexattr* attr, int protocol);
|
||||
int KYTY_SYSV_ABI PthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char* name);
|
||||
int KYTY_SYSV_ABI PthreadMutexDestroy(PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI PthreadMutexLock(PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI PthreadMutexTrylock(PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI PthreadMutexUnlock(PthreadMutex* mutex);
|
||||
|
||||
Pthread KYTY_SYSV_ABI PthreadSelf();
|
||||
int KYTY_SYSV_ABI PthreadCreate(Pthread* thread, const PthreadAttr* attr, pthread_entry_func_t entry, void* arg, const char* name);
|
||||
int KYTY_SYSV_ABI PthreadDetach(Pthread thread);
|
||||
int KYTY_SYSV_ABI PthreadJoin(Pthread thread, void** value);
|
||||
int KYTY_SYSV_ABI PthreadCancel(Pthread thread);
|
||||
int KYTY_SYSV_ABI PthreadSetcancelstate(int state, int* old_state);
|
||||
int KYTY_SYSV_ABI PthreadSetcanceltype(int type, int* old_type);
|
||||
void KYTY_SYSV_ABI PthreadTestcancel();
|
||||
void KYTY_SYSV_ABI PthreadExit(void* value);
|
||||
int KYTY_SYSV_ABI PthreadEqual(Pthread thread1, Pthread thread2);
|
||||
int KYTY_SYSV_ABI PthreadGetname(Pthread thread, char* name);
|
||||
int KYTY_SYSV_ABI KernelUsleep(KernelUseconds microseconds);
|
||||
unsigned int KYTY_SYSV_ABI KernelSleep(unsigned int seconds);
|
||||
int KYTY_SYSV_ABI KernelNanosleep(const KernelTimespec* rqtp, KernelTimespec* rmtp);
|
||||
|
||||
void KYTY_SYSV_ABI KernelSetThreadDtors(thread_dtors_func_t dtors);
|
||||
|
||||
int KYTY_SYSV_ABI PthreadAttrInit(PthreadAttr* attr);
|
||||
int KYTY_SYSV_ABI PthreadAttrDestroy(PthreadAttr* attr);
|
||||
int KYTY_SYSV_ABI PthreadAttrGet(Pthread thread, PthreadAttr* attr);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetaffinity(const PthreadAttr* attr, KernelCpumask* mask);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetdetachstate(const PthreadAttr* attr, int* state);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetguardsize(const PthreadAttr* attr, size_t* guard_size);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetinheritsched(const PthreadAttr* attr, int* inherit_sched);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetschedparam(const PthreadAttr* attr, KernelSchedParam* param);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetschedpolicy(const PthreadAttr* attr, int* policy);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetstack(const PthreadAttr* __restrict attr, void** __restrict stack_addr, size_t* __restrict stack_size);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetstackaddr(const PthreadAttr* attr, void** stack_addr);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetstacksize(const PthreadAttr* attr, size_t* stack_size);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetaffinity(PthreadAttr* attr, KernelCpumask mask);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetdetachstate(PthreadAttr* attr, int state);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetguardsize(PthreadAttr* attr, size_t guard_size);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetinheritsched(PthreadAttr* attr, int inherit_sched);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedParam* param);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetschedpolicy(PthreadAttr* attr, int policy);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetstack(PthreadAttr* attr, void* addr, size_t size);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetstackaddr(PthreadAttr* attr, void* addr);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetstacksize(PthreadAttr* attr, size_t stack_size);
|
||||
|
||||
int KYTY_SYSV_ABI PthreadRwlockDestroy(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockInit(PthreadRwlock* rwlock, const PthreadRwlockattr* attr, const char* name);
|
||||
int KYTY_SYSV_ABI PthreadRwlockRdlock(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockTimedrdlock(PthreadRwlock* rwlock, KernelUseconds usec);
|
||||
int KYTY_SYSV_ABI PthreadRwlockTimedwrlock(PthreadRwlock* rwlock, KernelUseconds usec);
|
||||
int KYTY_SYSV_ABI PthreadRwlockTryrdlock(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockTrywrlock(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockUnlock(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockWrlock(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockattrDestroy(PthreadRwlockattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadRwlockattrInit(PthreadRwlockattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadRwlockattrGettype(PthreadRwlockattr* attr, int* type);
|
||||
int KYTY_SYSV_ABI PthreadRwlockattrSettype(PthreadRwlockattr* attr, int type);
|
||||
|
||||
int KYTY_SYSV_ABI PthreadCondattrDestroy(PthreadCondattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadCondattrInit(PthreadCondattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadCondBroadcast(PthreadCond* cond);
|
||||
int KYTY_SYSV_ABI PthreadCondDestroy(PthreadCond* cond);
|
||||
int KYTY_SYSV_ABI PthreadCondInit(PthreadCond* cond, const PthreadCondattr* attr, const char* name);
|
||||
int KYTY_SYSV_ABI PthreadCondSignal(PthreadCond* cond);
|
||||
int KYTY_SYSV_ABI PthreadCondSignalto(PthreadCond* cond, Pthread thread);
|
||||
int KYTY_SYSV_ABI PthreadCondTimedwait(PthreadCond* cond, PthreadMutex* mutex, KernelUseconds usec);
|
||||
int KYTY_SYSV_ABI PthreadCondWait(PthreadCond* cond, PthreadMutex* mutex);
|
||||
|
||||
int KYTY_SYSV_ABI KernelClockGetres(KernelClockid clock_id, KernelTimespec* tp);
|
||||
int KYTY_SYSV_ABI KernelClockGettime(KernelClockid clock_id, KernelTimespec* tp);
|
||||
int KYTY_SYSV_ABI KernelGettimeofday(KernelTimeval* tp);
|
||||
uint64_t KYTY_SYSV_ABI KernelGetTscFrequency();
|
||||
uint64_t KYTY_SYSV_ABI KernelReadTsc();
|
||||
uint64_t KYTY_SYSV_ABI KernelGetProcessTime();
|
||||
uint64_t KYTY_SYSV_ABI KernelGetProcessTimeCounter();
|
||||
uint64_t KYTY_SYSV_ABI KernelGetProcessTimeCounterFrequency();
|
||||
|
||||
int KYTY_SYSV_ABI pthread_cond_broadcast_s(PthreadCond* cond);
|
||||
int KYTY_SYSV_ABI pthread_cond_wait_s(PthreadCond* cond, PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI pthread_mutex_lock_s(PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI pthread_mutex_unlock_s(PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI pthread_rwlock_rdlock_s(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI pthread_rwlock_unlock_s(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI pthread_rwlock_wrlock_s(PthreadRwlock* rwlock);
|
||||
|
||||
} // namespace Kyty::Libs::LibKernel
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_PTHREAD_H_ */
|
||||
@@ -0,0 +1,155 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_ERRNO_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_LIBS_ERRNO_H_
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
constexpr int OK = 0;
|
||||
|
||||
namespace Kyty::Libs::LibKernel {
|
||||
|
||||
constexpr int KERNEL_ERROR_UNKNOWN = -2147352576; /* 0x80020000 */
|
||||
constexpr int KERNEL_ERROR_EPERM = -2147352575; /* 0x80020001 */
|
||||
constexpr int KERNEL_ERROR_ENOENT = -2147352574; /* 0x80020002 */
|
||||
constexpr int KERNEL_ERROR_ESRCH = -2147352573; /* 0x80020003 */
|
||||
constexpr int KERNEL_ERROR_EINTR = -2147352572; /* 0x80020004 */
|
||||
constexpr int KERNEL_ERROR_EIO = -2147352571; /* 0x80020005 */
|
||||
constexpr int KERNEL_ERROR_ENXIO = -2147352570; /* 0x80020006 */
|
||||
constexpr int KERNEL_ERROR_E2BIG = -2147352569; /* 0x80020007 */
|
||||
constexpr int KERNEL_ERROR_ENOEXEC = -2147352568; /* 0x80020008 */
|
||||
constexpr int KERNEL_ERROR_EBADF = -2147352567; /* 0x80020009 */
|
||||
constexpr int KERNEL_ERROR_ECHILD = -2147352566; /* 0x8002000A */
|
||||
constexpr int KERNEL_ERROR_EDEADLK = -2147352565; /* 0x8002000B */
|
||||
constexpr int KERNEL_ERROR_ENOMEM = -2147352564; /* 0x8002000C */
|
||||
constexpr int KERNEL_ERROR_EACCES = -2147352563; /* 0x8002000D */
|
||||
constexpr int KERNEL_ERROR_EFAULT = -2147352562; /* 0x8002000E */
|
||||
constexpr int KERNEL_ERROR_ENOTBLK = -2147352561; /* 0x8002000F */
|
||||
constexpr int KERNEL_ERROR_EBUSY = -2147352560; /* 0x80020010 */
|
||||
constexpr int KERNEL_ERROR_EEXIST = -2147352559; /* 0x80020011 */
|
||||
constexpr int KERNEL_ERROR_EXDEV = -2147352558; /* 0x80020012 */
|
||||
constexpr int KERNEL_ERROR_ENODEV = -2147352557; /* 0x80020013 */
|
||||
constexpr int KERNEL_ERROR_ENOTDIR = -2147352556; /* 0x80020014 */
|
||||
constexpr int KERNEL_ERROR_EISDIR = -2147352555; /* 0x80020015 */
|
||||
constexpr int KERNEL_ERROR_EINVAL = -2147352554; /* 0x80020016 */
|
||||
constexpr int KERNEL_ERROR_ENFILE = -2147352553; /* 0x80020017 */
|
||||
constexpr int KERNEL_ERROR_EMFILE = -2147352552; /* 0x80020018 */
|
||||
constexpr int KERNEL_ERROR_ENOTTY = -2147352551; /* 0x80020019 */
|
||||
constexpr int KERNEL_ERROR_ETXTBSY = -2147352550; /* 0x8002001A */
|
||||
constexpr int KERNEL_ERROR_EFBIG = -2147352549; /* 0x8002001B */
|
||||
constexpr int KERNEL_ERROR_ENOSPC = -2147352548; /* 0x8002001C */
|
||||
constexpr int KERNEL_ERROR_ESPIPE = -2147352547; /* 0x8002001D */
|
||||
constexpr int KERNEL_ERROR_EROFS = -2147352546; /* 0x8002001E */
|
||||
constexpr int KERNEL_ERROR_EMLINK = -2147352545; /* 0x8002001F */
|
||||
constexpr int KERNEL_ERROR_EPIPE = -2147352544; /* 0x80020020 */
|
||||
constexpr int KERNEL_ERROR_EDOM = -2147352543; /* 0x80020021 */
|
||||
constexpr int KERNEL_ERROR_ERANGE = -2147352542; /* 0x80020022 */
|
||||
constexpr int KERNEL_ERROR_EAGAIN = -2147352541; /* 0x80020023 */
|
||||
constexpr int KERNEL_ERROR_EWOULDBLOCK = -2147352541; /* 0x80020023 */
|
||||
constexpr int KERNEL_ERROR_EINPROGRESS = -2147352540; /* 0x80020024 */
|
||||
constexpr int KERNEL_ERROR_EALREADY = -2147352539; /* 0x80020025 */
|
||||
constexpr int KERNEL_ERROR_ENOTSOCK = -2147352538; /* 0x80020026 */
|
||||
constexpr int KERNEL_ERROR_EDESTADDRREQ = -2147352537; /* 0x80020027 */
|
||||
constexpr int KERNEL_ERROR_EMSGSIZE = -2147352536; /* 0x80020028 */
|
||||
constexpr int KERNEL_ERROR_EPROTOTYPE = -2147352535; /* 0x80020029 */
|
||||
constexpr int KERNEL_ERROR_ENOPROTOOPT = -2147352534; /* 0x8002002A */
|
||||
constexpr int KERNEL_ERROR_EPROTONOSUPPORT = -2147352533; /* 0x8002002B */
|
||||
constexpr int KERNEL_ERROR_ESOCKTNOSUPPORT = -2147352532; /* 0x8002002C */
|
||||
constexpr int KERNEL_ERROR_EOPNOTSUPP = -2147352531; /* 0x8002002D */
|
||||
constexpr int KERNEL_ERROR_ENOTSUP = -2147352531; /* 0x8002002D */
|
||||
constexpr int KERNEL_ERROR_EPFNOSUPPORT = -2147352530; /* 0x8002002E */
|
||||
constexpr int KERNEL_ERROR_EAFNOSUPPORT = -2147352529; /* 0x8002002F */
|
||||
constexpr int KERNEL_ERROR_EADDRINUSE = -2147352528; /* 0x80020030 */
|
||||
constexpr int KERNEL_ERROR_EADDRNOTAVAIL = -2147352527; /* 0x80020031 */
|
||||
constexpr int KERNEL_ERROR_ENETDOWN = -2147352526; /* 0x80020032 */
|
||||
constexpr int KERNEL_ERROR_ENETUNREACH = -2147352525; /* 0x80020033 */
|
||||
constexpr int KERNEL_ERROR_ENETRESET = -2147352524; /* 0x80020034 */
|
||||
constexpr int KERNEL_ERROR_ECONNABORTED = -2147352523; /* 0x80020035 */
|
||||
constexpr int KERNEL_ERROR_ECONNRESET = -2147352522; /* 0x80020036 */
|
||||
constexpr int KERNEL_ERROR_ENOBUFS = -2147352521; /* 0x80020037 */
|
||||
constexpr int KERNEL_ERROR_EISCONN = -2147352520; /* 0x80020038 */
|
||||
constexpr int KERNEL_ERROR_ENOTCONN = -2147352519; /* 0x80020039 */
|
||||
constexpr int KERNEL_ERROR_ESHUTDOWN = -2147352518; /* 0x8002003A */
|
||||
constexpr int KERNEL_ERROR_ETOOMANYREFS = -2147352517; /* 0x8002003B */
|
||||
constexpr int KERNEL_ERROR_ETIMEDOUT = -2147352516; /* 0x8002003C */
|
||||
constexpr int KERNEL_ERROR_ECONNREFUSED = -2147352515; /* 0x8002003D */
|
||||
constexpr int KERNEL_ERROR_ELOOP = -2147352514; /* 0x8002003E */
|
||||
constexpr int KERNEL_ERROR_ENAMETOOLONG = -2147352513; /* 0x8002003F */
|
||||
constexpr int KERNEL_ERROR_EHOSTDOWN = -2147352512; /* 0x80020040 */
|
||||
constexpr int KERNEL_ERROR_EHOSTUNREACH = -2147352511; /* 0x80020041 */
|
||||
constexpr int KERNEL_ERROR_ENOTEMPTY = -2147352510; /* 0x80020042 */
|
||||
constexpr int KERNEL_ERROR_EPROCLIM = -2147352509; /* 0x80020043 */
|
||||
constexpr int KERNEL_ERROR_EUSERS = -2147352508; /* 0x80020044 */
|
||||
constexpr int KERNEL_ERROR_EDQUOT = -2147352507; /* 0x80020045 */
|
||||
constexpr int KERNEL_ERROR_ESTALE = -2147352506; /* 0x80020046 */
|
||||
constexpr int KERNEL_ERROR_EREMOTE = -2147352505; /* 0x80020047 */
|
||||
constexpr int KERNEL_ERROR_EBADRPC = -2147352504; /* 0x80020048 */
|
||||
constexpr int KERNEL_ERROR_ERPCMISMATCH = -2147352503; /* 0x80020049 */
|
||||
constexpr int KERNEL_ERROR_EPROGUNAVAIL = -2147352502; /* 0x8002004A */
|
||||
constexpr int KERNEL_ERROR_EPROGMISMATCH = -2147352501; /* 0x8002004B */
|
||||
constexpr int KERNEL_ERROR_EPROCUNAVAIL = -2147352500; /* 0x8002004C */
|
||||
constexpr int KERNEL_ERROR_ENOLCK = -2147352499; /* 0x8002004D */
|
||||
constexpr int KERNEL_ERROR_ENOSYS = -2147352498; /* 0x8002004E */
|
||||
constexpr int KERNEL_ERROR_EFTYPE = -2147352497; /* 0x8002004F */
|
||||
constexpr int KERNEL_ERROR_EAUTH = -2147352496; /* 0x80020050 */
|
||||
constexpr int KERNEL_ERROR_ENEEDAUTH = -2147352495; /* 0x80020051 */
|
||||
constexpr int KERNEL_ERROR_EIDRM = -2147352494; /* 0x80020052 */
|
||||
constexpr int KERNEL_ERROR_ENOMSG = -2147352493; /* 0x80020053 */
|
||||
constexpr int KERNEL_ERROR_EOVERFLOW = -2147352492; /* 0x80020054 */
|
||||
constexpr int KERNEL_ERROR_ECANCELED = -2147352491; /* 0x80020055 */
|
||||
constexpr int KERNEL_ERROR_EILSEQ = -2147352490; /* 0x80020056 */
|
||||
constexpr int KERNEL_ERROR_ENOATTR = -2147352489; /* 0x80020057 */
|
||||
constexpr int KERNEL_ERROR_EDOOFUS = -2147352488; /* 0x80020058 */
|
||||
constexpr int KERNEL_ERROR_EBADMSG = -2147352487; /* 0x80020059 */
|
||||
constexpr int KERNEL_ERROR_EMULTIHOP = -2147352486; /* 0x8002005A */
|
||||
constexpr int KERNEL_ERROR_ENOLINK = -2147352485; /* 0x8002005B */
|
||||
constexpr int KERNEL_ERROR_EPROTO = -2147352484; /* 0x8002005C */
|
||||
constexpr int KERNEL_ERROR_ENOTCAPABLE = -2147352483; /* 0x8002005D */
|
||||
constexpr int KERNEL_ERROR_ECAPMODE = -2147352482; /* 0x8002005E */
|
||||
constexpr int KERNEL_ERROR_ENOBLK = -2147352481; /* 0x8002005F */
|
||||
constexpr int KERNEL_ERROR_EICV = -2147352480; /* 0x80020060 */
|
||||
constexpr int KERNEL_ERROR_ENOPLAYGOENT = -2147352479; /* 0x80020061 */
|
||||
constexpr int KERNEL_ERROR_EREVOKE = -2147352478; /* 0x80020062 */
|
||||
constexpr int KERNEL_ERROR_ESDKVERSION = -2147352477; /* 0x80020063 */
|
||||
constexpr int KERNEL_ERROR_ESTART = -2147352476; /* 0x80020064 */
|
||||
constexpr int KERNEL_ERROR_ESTOP = -2147352475; /* 0x80020065 */
|
||||
|
||||
} // namespace Kyty::Libs::LibKernel
|
||||
|
||||
namespace Kyty::Libs::VideoOut {
|
||||
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_VALUE = -2144796671; /* 0x80290001 */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_ADDRESS = -2144796670; /* 0x80290002 */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_PIXEL_FORMAT = -2144796669; /* 0x80290003 */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_PITCH = -2144796668; /* 0x80290004 */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_RESOLUTION = -2144796667; /* 0x80290005 */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_FLIP_MODE = -2144796666; /* 0x80290006 */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_TILING_MODE = -2144796665; /* 0x80290007 */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_ASPECT_RATIO = -2144796664; /* 0x80290008 */
|
||||
constexpr int VIDEO_OUT_ERROR_RESOURCE_BUSY = -2144796663; /* 0x80290009 */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_INDEX = -2144796662; /* 0x8029000A */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_HANDLE = -2144796661; /* 0x8029000B */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = -2144796660; /* 0x8029000C */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_EVENT = -2144796659; /* 0x8029000D */
|
||||
constexpr int VIDEO_OUT_ERROR_NO_EMPTY_SLOT = -2144796657; /* 0x8029000F */
|
||||
constexpr int VIDEO_OUT_ERROR_SLOT_OCCUPIED = -2144796656; /* 0x80290010 */
|
||||
constexpr int VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = -2144796654; /* 0x80290012 */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_MEMORY = -2144796653; /* 0x80290013 */
|
||||
constexpr int VIDEO_OUT_ERROR_MEMORY_NOT_PHYSICALLY_CONTIGUOUS = -2144796652; /* 0x80290014 */
|
||||
constexpr int VIDEO_OUT_ERROR_MEMORY_INVALID_ALIGNMENT = -2144796651; /* 0x80290015 */
|
||||
constexpr int VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE = -2144796650; /* 0x80290016 */
|
||||
constexpr int VIDEO_OUT_ERROR_OVERFLOW = -2144796649; /* 0x80290017 */
|
||||
constexpr int VIDEO_OUT_ERROR_NO_DEVICE = -2144796648; /* 0x80290018 */
|
||||
constexpr int VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE = -2144796647; /* 0x80290019 */
|
||||
constexpr int VIDEO_OUT_ERROR_INVALID_OPTION = -2144796646; /* 0x8029001A */
|
||||
constexpr int VIDEO_OUT_ERROR_PORT_UNSUPPORTED_FUNCTION = -2144796645; /* 0x8029001B */
|
||||
constexpr int VIDEO_OUT_ERROR_UNSUPPORTED_OPERATION = -2144796644; /* 0x8029001C */
|
||||
constexpr int VIDEO_OUT_ERROR_FATAL = -2144796417; /* 0x802900FF */
|
||||
constexpr int VIDEO_OUT_ERROR_UNKNOWN = -2144796418; /* 0x802900FE */
|
||||
constexpr int VIDEO_OUT_ERROR_ENOMEM = -2144792564; /* 0x8029100C */
|
||||
|
||||
} // namespace Kyty::Libs::VideoOut
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_LIBS_ERRNO_H_ */
|
||||
@@ -0,0 +1,87 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_LIBS_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_LIBS_LIBS_H_
|
||||
|
||||
#include "Kyty/Core/String.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/Timer.h" // IWYU pragma: keep
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define PRINT_NAME_ENABLED g_print_name
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define PRINT_NAME_ENABLE(flag) PRINT_NAME_ENABLED = flag;
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define LIB_DEFINE(name) void name(Loader::SymbolDatabase* s)
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define LIB_NAME(l, m) \
|
||||
static thread_local bool PRINT_NAME_ENABLED = true; \
|
||||
static constexpr char g_library[] = l; \
|
||||
static constexpr char g_module[] = m;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define LIB_VERSION(l, lv, m, mv1, mv2) \
|
||||
LIB_NAME(l, m); \
|
||||
static constexpr int g_library_version = lv; \
|
||||
static constexpr int g_module_version_major = mv1; \
|
||||
static constexpr int g_module_version_minor = mv2;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define LIB_USING(n) \
|
||||
using n::g_library; \
|
||||
using n::g_library_version; \
|
||||
using n::g_module; \
|
||||
using n::g_module_version_major; \
|
||||
using n::g_module_version_minor;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define LIB_CHECK(ids, name) \
|
||||
if (id == (ids)) \
|
||||
{ \
|
||||
name(s); \
|
||||
return true; \
|
||||
}
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define LIB_ADD(n, f, t) \
|
||||
{ \
|
||||
Loader::SymbolResolve sr {}; \
|
||||
sr.name = n; \
|
||||
sr.library = g_library; \
|
||||
sr.library_version = g_library_version; \
|
||||
sr.module = g_module; \
|
||||
sr.module_version_major = g_module_version_major; \
|
||||
sr.module_version_minor = g_module_version_minor; \
|
||||
sr.type = t; \
|
||||
auto func = reinterpret_cast<uint64_t>(f); \
|
||||
const char32_t* dbg_name = U"" #f; \
|
||||
s->Add(sr, func, dbg_name); \
|
||||
}
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define LIB_OBJECT(n, f) LIB_ADD(n, f, Loader::SymbolType::Object)
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define LIB_FUNC(n, f) LIB_ADD(n, f, Loader::SymbolType::Func)
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define PRINT_NAME() \
|
||||
if (PRINT_NAME_ENABLED) \
|
||||
{ \
|
||||
Kyty::printf(FG_CYAN "[%d][%s] %s::%s::%s()" DEFAULT "\n", Core::Thread::GetThreadIdUnique(), \
|
||||
Loader::Timer::GetTime().ToString("HH24:MI:SS.FFF").C_Str(), g_library, g_module, __func__); \
|
||||
}
|
||||
|
||||
namespace Kyty {
|
||||
|
||||
namespace Loader {
|
||||
class SymbolDatabase;
|
||||
} // namespace Loader
|
||||
|
||||
namespace Libs {
|
||||
|
||||
bool Init(const String& id, Loader::SymbolDatabase* s);
|
||||
|
||||
} // namespace Libs
|
||||
} // namespace Kyty
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_LIBS_LIBS_H_ */
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_PRINTF_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_LIBS_PRINTF_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs {
|
||||
|
||||
struct VaContext;
|
||||
struct VaList;
|
||||
|
||||
using libc_print_func_t = KYTY_FORMAT_PRINTF(1, 2) KYTY_SYSV_ABI int (*)(const char* str, ...);
|
||||
using libc_print_v_func_t = int (*)(VaContext* c);
|
||||
using libc_vprint_func_t = int (*)(const char* str, VaList* c);
|
||||
|
||||
libc_print_func_t GetPrintFunc();
|
||||
libc_print_v_func_t GetPrintFuncV();
|
||||
libc_vprint_func_t GetVPrintFunc();
|
||||
|
||||
} // namespace Kyty::Libs
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_LIBS_PRINTF_H_ */
|
||||
@@ -0,0 +1,241 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_VACONTEXT_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_LIBS_VACONTEXT_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <xmmintrin.h>
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define VA_ARGS \
|
||||
uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, uint64_t overflow_arg_area, __m128 xmm0, \
|
||||
__m128 xmm1, __m128 xmm2, __m128 xmm3, __m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7, ...
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define VA_CONTEXT(ctx) \
|
||||
alignas(16) VaContext ctx; \
|
||||
(ctx).reg_save_area.gp[0] = rdi; \
|
||||
(ctx).reg_save_area.gp[1] = rsi; \
|
||||
(ctx).reg_save_area.gp[2] = rdx; \
|
||||
(ctx).reg_save_area.gp[3] = rcx; \
|
||||
(ctx).reg_save_area.gp[4] = r8; \
|
||||
(ctx).reg_save_area.gp[5] = r9; \
|
||||
(ctx).reg_save_area.fp[0] = xmm0; \
|
||||
(ctx).reg_save_area.fp[1] = xmm1; \
|
||||
(ctx).reg_save_area.fp[2] = xmm2; \
|
||||
(ctx).reg_save_area.fp[3] = xmm3; \
|
||||
(ctx).reg_save_area.fp[4] = xmm4; \
|
||||
(ctx).reg_save_area.fp[5] = xmm5; \
|
||||
(ctx).reg_save_area.fp[6] = xmm6; \
|
||||
(ctx).reg_save_area.fp[7] = xmm7; \
|
||||
(ctx).va_list.reg_save_area = &(ctx).reg_save_area; \
|
||||
(ctx).va_list.gp_offset = offsetof(VaRegSave, gp); \
|
||||
(ctx).va_list.fp_offset = offsetof(VaRegSave, fp); \
|
||||
(ctx).va_list.overflow_arg_area = &overflow_arg_area;
|
||||
|
||||
namespace Kyty::Libs {
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct VaList
|
||||
{
|
||||
uint32_t gp_offset;
|
||||
uint32_t fp_offset;
|
||||
void* overflow_arg_area;
|
||||
void* reg_save_area;
|
||||
};
|
||||
|
||||
// typedef float __m128 __attribute__((__vector_size__(16), __aligned__(16)));
|
||||
|
||||
struct VaRegSave
|
||||
{
|
||||
uint64_t gp[6];
|
||||
__m128 fp[8];
|
||||
};
|
||||
|
||||
struct VaContext
|
||||
{
|
||||
VaRegSave reg_save_area;
|
||||
VaList va_list;
|
||||
};
|
||||
|
||||
struct VaCharX16
|
||||
{
|
||||
char x[16];
|
||||
};
|
||||
|
||||
struct VaShortX8
|
||||
{
|
||||
short x[8]; // NOLINT(google-runtime-int)
|
||||
};
|
||||
|
||||
struct VaIntX4
|
||||
{
|
||||
int x[4];
|
||||
};
|
||||
|
||||
struct VaFloatX4
|
||||
{
|
||||
float x[4];
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
template <class T, uint64_t Align, uint64_t Size>
|
||||
T VaArg_overflow_arg_area(VaList* l)
|
||||
{
|
||||
auto ptr = ((reinterpret_cast<uint64_t>(l->overflow_arg_area) + (Align - 1)) & ~(Align - 1));
|
||||
auto* addr = reinterpret_cast<T*>(ptr);
|
||||
l->overflow_arg_area = reinterpret_cast<void*>(ptr + Size);
|
||||
return *addr;
|
||||
}
|
||||
|
||||
template <class T, uint32_t Size>
|
||||
T VaArg_reg_save_area_gp(VaList* l)
|
||||
{
|
||||
auto* addr = reinterpret_cast<T*>(static_cast<uint8_t*>(l->reg_save_area) + l->gp_offset);
|
||||
l->gp_offset += Size;
|
||||
return *addr;
|
||||
}
|
||||
|
||||
template <class T, uint32_t Size>
|
||||
T VaArg_reg_save_area_fp(VaList* l)
|
||||
{
|
||||
auto* addr = reinterpret_cast<T*>(static_cast<uint8_t*>(l->reg_save_area) + l->fp_offset);
|
||||
l->fp_offset += Size;
|
||||
return *addr;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline VaFloatX4 VaArg_reg_save_area_fp<VaFloatX4, 32>(VaList* l)
|
||||
{
|
||||
auto* addr = reinterpret_cast<VaFloatX4*>(static_cast<uint8_t*>(l->reg_save_area) + l->fp_offset);
|
||||
l->fp_offset += 32;
|
||||
VaFloatX4 ret = {{addr[0].x[0], addr[0].x[1], addr[1].x[0], addr[1].x[1]}};
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline int VaArg_int(VaList* l)
|
||||
{
|
||||
if (l->gp_offset <= 40)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<int, 8>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<int, 1, 8>(l);
|
||||
}
|
||||
|
||||
inline double VaArg_double(VaList* l)
|
||||
{
|
||||
if (l->fp_offset <= 160)
|
||||
{
|
||||
return VaArg_reg_save_area_fp<double, 16>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<double, 1, 8>(l);
|
||||
}
|
||||
|
||||
inline long double VaArg_long_double(VaList* l)
|
||||
{
|
||||
return VaArg_overflow_arg_area<long double, 16, 16>(l);
|
||||
}
|
||||
|
||||
inline wint_t VaArg_wint_t(VaList* l)
|
||||
{
|
||||
if (l->gp_offset <= 40)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<wint_t, 8>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<wint_t, 1, 8>(l);
|
||||
}
|
||||
|
||||
inline VaCharX16 VaArg_char_x16(VaList* l)
|
||||
{
|
||||
if (l->gp_offset <= 32)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<VaCharX16, 16>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<VaCharX16, 1, 16>(l);
|
||||
}
|
||||
|
||||
inline long VaArg_long(VaList* l) // NOLINT(google-runtime-int)
|
||||
{
|
||||
if (l->gp_offset <= 40)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<long, 8>(l); // NOLINT(google-runtime-int)
|
||||
}
|
||||
return VaArg_overflow_arg_area<long, 1, 8>(l); // NOLINT(google-runtime-int)
|
||||
}
|
||||
|
||||
inline intmax_t VaArg_intmax_t(VaList* l)
|
||||
{
|
||||
if (l->gp_offset <= 40)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<intmax_t, 8>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<intmax_t, 1, 8>(l);
|
||||
}
|
||||
|
||||
inline long long VaArg_long_long(VaList* l) // NOLINT(google-runtime-int)
|
||||
{
|
||||
if (l->gp_offset <= 40)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<long long, 8>(l); // NOLINT(google-runtime-int)
|
||||
}
|
||||
return VaArg_overflow_arg_area<long long, 1, 8>(l); // NOLINT(google-runtime-int)
|
||||
}
|
||||
|
||||
inline ptrdiff_t VaArg_ptrdiff_t(VaList* l)
|
||||
{
|
||||
if (l->gp_offset <= 40)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<ptrdiff_t, 8>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<ptrdiff_t, 1, 8>(l);
|
||||
}
|
||||
|
||||
inline size_t VaArg_size_t(VaList* l)
|
||||
{
|
||||
if (l->gp_offset <= 40)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<size_t, 8>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<size_t, 1, 8>(l);
|
||||
}
|
||||
|
||||
inline VaShortX8 VaArg_ShortX8(VaList* l)
|
||||
{
|
||||
if (l->gp_offset <= 32)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<VaShortX8, 16>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<VaShortX8, 1, 16>(l);
|
||||
}
|
||||
|
||||
inline VaIntX4 VaArg_IntX4(VaList* l)
|
||||
{
|
||||
if (l->gp_offset <= 32)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<VaIntX4, 16>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<VaIntX4, 1, 16>(l);
|
||||
}
|
||||
|
||||
inline VaFloatX4 VaArg_FloatX4(VaList* l)
|
||||
{
|
||||
if (l->fp_offset <= 144)
|
||||
{
|
||||
return VaArg_reg_save_area_fp<VaFloatX4, 32>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<VaFloatX4, 1, 16>(l);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* VaArg_ptr(VaList* l)
|
||||
{
|
||||
if (l->gp_offset <= 40)
|
||||
{
|
||||
return VaArg_reg_save_area_gp<T*, 8>(l);
|
||||
}
|
||||
return VaArg_overflow_arg_area<T*, 1, 8>(l);
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_LIBS_VACONTEXT_H_ */
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_LOG_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_LOG_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/File.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
//#include "Emulator/Config.h"
|
||||
|
||||
//#define KYTY_LOG_ENABLED
|
||||
|
||||
#define CSI "\x1b[" // NOLINT
|
||||
#define DEFAULT CSI "0m" // NOLINT // Returns all attributes to the default state prior to modification
|
||||
#define BOLD CSI "1m" // NOLINT // Applies brightness/intensity flag to foreground color
|
||||
#define NO_BOLD CSI "22m" // NOLINT // Removes brightness/intensity flag from foreground color
|
||||
#define UNDERLINE CSI "4m" // NOLINT // Adds underline
|
||||
#define NO_UNDERLINE CSI "24m" // NOLINT // Removes underline
|
||||
#define NEGATIVE CSI "7m" // NOLINT // Swaps foreground and background colors
|
||||
#define POSITIVE CSI "27m" // NOLINT // Returns foreground/background to normal
|
||||
#define FG_BLACK CSI "30m" // NOLINT // Applies non-bold/bright black to foreground
|
||||
#define FG_RED CSI "31m" // NOLINT // Applies non-bold/bright red to foreground
|
||||
#define FG_GREEN CSI "32m" // NOLINT // Applies non-bold/bright green to foreground
|
||||
#define FG_YELLOW CSI "33m" // NOLINT // Applies non-bold/bright yellow to foreground
|
||||
#define FG_BLUE CSI "34m" // NOLINT // Applies non-bold/bright blue to foreground
|
||||
#define FG_MAGENTA CSI "35m" // NOLINT // Applies non-bold/bright magenta to foreground
|
||||
#define FG_CYAN CSI "36m" // NOLINT // Applies non-bold/bright cyan to foreground
|
||||
#define FG_WHITE CSI "37m" // NOLINT // Applies non-bold/bright white to foreground
|
||||
#define FG_EXTENDED CSI "38m" // NOLINT // Applies extended color value to the foreground (see details below)
|
||||
#define FG_DEFAULT CSI "39m" // NOLINT // Applies only the foreground portion of the defaults (see 0)
|
||||
#define BG_BLACK CSI "40m" // NOLINT // Applies non-bold/bright black to background
|
||||
#define BG_RED CSI "41m" // NOLINT // Applies non-bold/bright red to background
|
||||
#define BG_GREEN CSI "42m" // NOLINT // Applies non-bold/bright green to background
|
||||
#define BG_YELLOW CSI "43m" // NOLINT // Applies non-bold/bright yellow to background
|
||||
#define BG_BLUE CSI "44m" // NOLINT // Applies non-bold/bright blue to background
|
||||
#define BG_MAGENTA CSI "45m" // NOLINT // Applies non-bold/bright magenta to background
|
||||
#define BG_CYAN CSI "46m" // NOLINT // Applies non-bold/bright cyan to background
|
||||
#define BG_WHITE CSI "47m" // NOLINT // Applies non-bold/bright white to background
|
||||
#define BG_EXTENDED CSI "48m" // NOLINT // Applies extended color value to the background (see details below)
|
||||
#define BG_DEFAULT CSI "49m" // NOLINT // Applies only the background portion of the defaults (see 0)
|
||||
#define FG_BRIGHT_BLACK CSI "90m" // NOLINT // Applies bold/bright black to foreground
|
||||
#define FG_BRIGHT_RED CSI "91m" // NOLINT // Applies bold/bright red to foreground
|
||||
#define FG_BRIGHT_GREEN CSI "92m" // NOLINT // Applies bold/bright green to foreground
|
||||
#define FG_BRIGHT_YELLOW CSI "93m" // NOLINT // Applies bold/bright yellow to foreground
|
||||
#define FG_BRIGHT_BLUE CSI "94m" // NOLINT // Applies bold/bright blue to foreground
|
||||
#define FG_BRIGHT_MAGENTA CSI "95m" // NOLINT // Applies bold/bright magenta to foreground
|
||||
#define FG_BRIGHT_CYAN CSI "96m" // NOLINT // Applies bold/bright cyan to foreground
|
||||
#define FG_BRIGHT_WHITE CSI "97m" // NOLINT // Applies bold/bright white to foreground
|
||||
#define BG_BRIGHT_BLACK CSI "100m" // NOLINT // Applies bold/bright black to background
|
||||
#define BG_BRIGHT_RED CSI "101m" // NOLINT // Applies bold/bright red to background
|
||||
#define BG_BRIGHT_GREEN CSI "102m" // NOLINT // Applies bold/bright green to background
|
||||
#define BG_BRIGHT_YELLOW CSI "103m" // NOLINT // Applies bold/bright yellow to background
|
||||
#define BG_BRIGHT_BLUE CSI "104m" // NOLINT // Applies bold/bright blue to background
|
||||
#define BG_BRIGHT_MAGENTA CSI "105m" // NOLINT // Applies bold/bright magenta to background
|
||||
#define BG_BRIGHT_CYAN CSI "106m" // NOLINT // Applies bold/bright cyan to background
|
||||
#define BG_BRIGHT_WHITE CSI "107m" // NOLINT // Applies bold/bright white to background
|
||||
|
||||
namespace Kyty {
|
||||
|
||||
namespace Log {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Log);
|
||||
|
||||
enum class Direction
|
||||
{
|
||||
Silent,
|
||||
Console,
|
||||
File
|
||||
};
|
||||
|
||||
void SetDirection(Direction dir);
|
||||
void SetOutputFile(const String& file_name, Core::File::Encoding enc = Core::File::Encoding::Utf8);
|
||||
|
||||
bool IsColoredPrintf();
|
||||
String RemoveColors(const String& str);
|
||||
|
||||
} // namespace Log
|
||||
|
||||
void printf(const char* format, ...) KYTY_FORMAT_PRINTF(1, 2);
|
||||
|
||||
} // namespace Kyty
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_LOG_H_ */
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_PROFILER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_PROFILER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
#define BUILD_WITH_EASY_PROFILER
|
||||
#define EASY_PROFILER_STATIC
|
||||
|
||||
#include <easy/profiler.h> // IWYU pragma: export
|
||||
|
||||
//
|
||||
#include "easy/details/profiler_aux.h" // IWYU pragma: export
|
||||
#include "easy/details/profiler_colors.h" // IWYU pragma: export
|
||||
|
||||
#include <cwchar> // IWYU pragma: export
|
||||
|
||||
// IWYU pragma: no_include "easy/profiler.h"
|
||||
|
||||
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
|
||||
#define KYTY_PROFILER_BLOCK(f, ...) EASY_BLOCK(f, __VA_ARGS__);
|
||||
#else
|
||||
#define KYTY_PROFILER_BLOCK(f, s...) EASY_BLOCK(f, ##s);
|
||||
#endif
|
||||
|
||||
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
|
||||
#define KYTY_PROFILER_FUNCTION(f, ...) EASY_FUNCTION(f, __VA_ARGS__);
|
||||
#else
|
||||
#define KYTY_PROFILER_FUNCTION(f, s...) EASY_FUNCTION(f, ##s);
|
||||
#endif
|
||||
|
||||
#define KYTY_PROFILER_END_BLOCK EASY_END_BLOCK
|
||||
|
||||
#define KYTY_PROFILER_THREAD(f) EASY_THREAD(f)
|
||||
|
||||
namespace Kyty::Profiler {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Profiler);
|
||||
|
||||
} // namespace Kyty::Profiler
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_PROFILER_H_ */
|
||||
@@ -0,0 +1,185 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_RUNTIMELINKER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_RUNTIMELINKER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/Hashmap.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
#include "Kyty/Core/Vector.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
#include "Emulator/SymbolDatabase.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Loader {
|
||||
|
||||
class Elf64;
|
||||
struct Elf64_Sym;
|
||||
struct Elf64_Rela;
|
||||
class RuntimeLinker;
|
||||
|
||||
namespace VirtualMemory {
|
||||
class ExceptionHandler;
|
||||
} // namespace VirtualMemory
|
||||
|
||||
using module_func_t = int (*)(size_t args, const void* argp);
|
||||
|
||||
struct ModuleId
|
||||
{
|
||||
bool operator==(const ModuleId& other) const
|
||||
{
|
||||
return version_major == other.version_major && version_minor == other.version_minor && name == other.name;
|
||||
}
|
||||
|
||||
String id;
|
||||
int version_major;
|
||||
int version_minor;
|
||||
String name;
|
||||
};
|
||||
|
||||
struct LibraryId
|
||||
{
|
||||
bool operator==(const LibraryId& other) const { return version == other.version && name == other.name; }
|
||||
|
||||
String id;
|
||||
int version;
|
||||
String name;
|
||||
};
|
||||
|
||||
struct ThreadLocalStorage
|
||||
{
|
||||
uint64_t image_vaddr = 0;
|
||||
uint64_t image_size = 0;
|
||||
uint64_t handler_vaddr = 0;
|
||||
|
||||
Core::Hashmap<int, uint8_t*> tlss;
|
||||
Core::Mutex mutex;
|
||||
};
|
||||
|
||||
struct DynamicInfo
|
||||
{
|
||||
void* hash_table = nullptr;
|
||||
uint64_t hash_table_size = 0;
|
||||
|
||||
char* str_table = nullptr;
|
||||
uint64_t str_table_size = 0;
|
||||
|
||||
Elf64_Sym* symbol_table = nullptr;
|
||||
uint64_t symbol_table_total_size = 0;
|
||||
uint64_t symbol_table_entry_size = 0;
|
||||
|
||||
uint64_t init_vaddr = 0;
|
||||
uint64_t fini_vaddr = 0;
|
||||
uint64_t init_array_vaddr = 0;
|
||||
uint64_t fini_array_vaddr = 0;
|
||||
uint64_t preinit_array_vaddr = 0;
|
||||
uint64_t init_array_size = 0;
|
||||
uint64_t fini_array_size = 0;
|
||||
uint64_t preinit_array_size = 0;
|
||||
uint64_t pltgot_vaddr = 0;
|
||||
|
||||
Elf64_Rela* jmprela_table = nullptr;
|
||||
uint64_t jmprela_table_size = 0;
|
||||
|
||||
Elf64_Rela* rela_table = nullptr;
|
||||
uint64_t rela_table_total_size = 0;
|
||||
uint64_t rela_table_entry_size = 0;
|
||||
|
||||
uint64_t relative_count = 0;
|
||||
|
||||
uint64_t debug = 0;
|
||||
uint64_t textrel = 0;
|
||||
uint64_t flags = 0;
|
||||
|
||||
const char* so_name = nullptr;
|
||||
|
||||
Vector<const char*> needed;
|
||||
Vector<ModuleId> export_modules;
|
||||
Vector<ModuleId> import_modules;
|
||||
Vector<LibraryId> export_libs;
|
||||
Vector<LibraryId> import_libs;
|
||||
};
|
||||
|
||||
struct Program
|
||||
{
|
||||
int32_t unique_id = -1;
|
||||
RuntimeLinker* rt = nullptr;
|
||||
String file_name;
|
||||
Elf64* elf = nullptr;
|
||||
VirtualMemory::ExceptionHandler* exception_handler = nullptr;
|
||||
DynamicInfo* dynamic_info = nullptr;
|
||||
uint64_t base_vaddr = 0;
|
||||
uint64_t base_size = 0;
|
||||
uint64_t base_size_aligned = 0;
|
||||
SymbolDatabase* export_symbols = nullptr;
|
||||
SymbolDatabase* import_symbols = nullptr;
|
||||
ThreadLocalStorage tls;
|
||||
bool fail_if_global_not_resolved = true;
|
||||
bool dbg_print_reloc = false;
|
||||
uint64_t proc_param_vaddr = 0;
|
||||
uint64_t custom_call_plt_vaddr = 0;
|
||||
uint32_t custom_call_plt_num = 0;
|
||||
};
|
||||
|
||||
class RuntimeLinker
|
||||
{
|
||||
public:
|
||||
RuntimeLinker();
|
||||
virtual ~RuntimeLinker();
|
||||
void Clear();
|
||||
|
||||
KYTY_CLASS_NO_COPY(RuntimeLinker);
|
||||
|
||||
void DbgDump(const String& folder);
|
||||
|
||||
Program* LoadProgram(const String& elf_name);
|
||||
void UnloadProgram(Program* program);
|
||||
|
||||
[[nodiscard]] uint64_t GetEntry();
|
||||
[[nodiscard]] uint64_t GetProcParam();
|
||||
|
||||
void RelocateAll();
|
||||
|
||||
void Execute();
|
||||
int StartModule(Program* program, size_t args, const void* argp, module_func_t func);
|
||||
int StopModule(Program* program, size_t args, const void* argp, module_func_t func);
|
||||
void StartAllModules();
|
||||
void StopAllModules();
|
||||
void DeleteTlss(int thread_id);
|
||||
|
||||
void Resolve(const String& name, SymbolType type, Program* program, SymbolRecord* out_info, bool* bind_self);
|
||||
|
||||
SymbolDatabase* Symbols() { return m_symbols; }
|
||||
|
||||
static uint64_t ReadFromElf(Program* program, uint64_t vaddr);
|
||||
Program* FindProgramByAddr(uint64_t vaddr);
|
||||
Program* FindProgramById(int32_t id);
|
||||
|
||||
static uint8_t* TlsGetAddr(Program* program);
|
||||
static void DeleteTls(Program* program, int thread_id);
|
||||
|
||||
private:
|
||||
static void LoadProgramToMemory(Program* program);
|
||||
static void ParseProgramDynamicInfo(Program* program);
|
||||
static void CreateSymbolDatabase(Program* program);
|
||||
static void Relocate(Program* program);
|
||||
static void DeleteProgram(Program* program);
|
||||
static void SetupTlsHandler(Program* program);
|
||||
|
||||
Program* FindProgram(const ModuleId& m, const LibraryId& l);
|
||||
|
||||
static const ModuleId* FindModule(const Program& program, const String& id);
|
||||
static const LibraryId* FindLibrary(const Program& program, const String& id);
|
||||
|
||||
Vector<Program*> m_programs;
|
||||
SymbolDatabase* m_symbols = nullptr;
|
||||
bool m_relocated = false;
|
||||
Core::Mutex m_mutex;
|
||||
};
|
||||
|
||||
} // namespace Kyty::Loader
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_RUNTIMELINKER_H_ */
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_SYMBOLDATABASE_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_SYMBOLDATABASE_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/Hashmap.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Vector.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Loader {
|
||||
|
||||
enum class SymbolType
|
||||
{
|
||||
Unknown,
|
||||
Func,
|
||||
Object,
|
||||
TlsModule
|
||||
};
|
||||
|
||||
struct SymbolRecord
|
||||
{
|
||||
String name;
|
||||
String dbg_name;
|
||||
uint64_t vaddr;
|
||||
};
|
||||
|
||||
struct SymbolResolve
|
||||
{
|
||||
String name;
|
||||
String library;
|
||||
int library_version;
|
||||
String module;
|
||||
int module_version_major;
|
||||
int module_version_minor;
|
||||
SymbolType type;
|
||||
};
|
||||
|
||||
class SymbolDatabase
|
||||
{
|
||||
public:
|
||||
SymbolDatabase() = default;
|
||||
virtual ~SymbolDatabase() = default;
|
||||
|
||||
void Add(const SymbolResolve& s, uint64_t vaddr);
|
||||
void Add(const SymbolResolve& s, uint64_t vaddr, const String& dbg_name);
|
||||
|
||||
[[nodiscard]] const SymbolRecord* Find(const SymbolResolve& s) const;
|
||||
|
||||
void DbgDump(const String& folder, const String& file_name);
|
||||
|
||||
KYTY_CLASS_NO_COPY(SymbolDatabase);
|
||||
|
||||
static String GenerateName(const SymbolResolve& s);
|
||||
|
||||
private:
|
||||
Vector<SymbolRecord> m_symbols;
|
||||
Core::Hashmap<String, uint32_t> m_map;
|
||||
};
|
||||
|
||||
} // namespace Kyty::Loader
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_SYMBOLDATABASE_H_ */
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_TIMER_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_TIMER_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/DateTime.h"
|
||||
#include "Kyty/Core/Subsystems.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Loader::Timer {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Timer);
|
||||
|
||||
void Start();
|
||||
double GetTimeMs();
|
||||
Core::Time GetTime();
|
||||
uint64_t GetCounter();
|
||||
uint64_t GetFrequency();
|
||||
|
||||
} // namespace Kyty::Loader::Timer
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_TIMER_H_ */
|
||||
@@ -0,0 +1,107 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_VIRTUALMEMORY_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_VIRTUALMEMORY_H_
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#include "Emulator/Common.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Loader {
|
||||
|
||||
enum class ProcessorArchitecture
|
||||
{
|
||||
Unknown,
|
||||
Amd64, // x64 (AMD or Intel)
|
||||
};
|
||||
|
||||
struct SystemInfo
|
||||
{
|
||||
uint32_t PageSize;
|
||||
uint64_t MinimumApplicationAddress;
|
||||
uint64_t MaximumApplicationAddress;
|
||||
uint32_t ActiveProcessorMask;
|
||||
uint32_t NumberOfProcessors;
|
||||
ProcessorArchitecture ProcessorArchitecture;
|
||||
uint32_t AllocationGranularity;
|
||||
uint16_t ProcessorLevel;
|
||||
uint16_t ProcessorRevision;
|
||||
};
|
||||
|
||||
SystemInfo GetSystemInfo();
|
||||
|
||||
namespace VirtualMemory {
|
||||
|
||||
class ExceptionHandlerPrivate;
|
||||
|
||||
class ExceptionHandler
|
||||
{
|
||||
public:
|
||||
enum class ExceptionType
|
||||
{
|
||||
Unknown,
|
||||
AccessViolation
|
||||
};
|
||||
|
||||
enum class AccessViolationType
|
||||
{
|
||||
Unknown,
|
||||
Read,
|
||||
Write,
|
||||
Execute
|
||||
};
|
||||
|
||||
struct ExceptionInfo
|
||||
{
|
||||
ExceptionType type = ExceptionType::Unknown;
|
||||
AccessViolationType access_violation_type = AccessViolationType::Unknown;
|
||||
uint64_t access_violation_vaddr = 0;
|
||||
};
|
||||
|
||||
using handler_func_t = void (*)(const ExceptionInfo*);
|
||||
|
||||
ExceptionHandler();
|
||||
virtual ~ExceptionHandler();
|
||||
|
||||
KYTY_CLASS_NO_COPY(ExceptionHandler);
|
||||
|
||||
static uint64_t GetSize();
|
||||
|
||||
bool Install(uint64_t base_address, uint64_t handler_addr, uint64_t image_size, handler_func_t func);
|
||||
bool Uninstall();
|
||||
|
||||
private:
|
||||
ExceptionHandlerPrivate* m_p = nullptr;
|
||||
};
|
||||
|
||||
enum class Mode : uint32_t
|
||||
{
|
||||
NoAccess = 0,
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
ReadWrite = Read | Write,
|
||||
Execute = 4,
|
||||
ExecuteRead = Execute | Read,
|
||||
ExecuteWrite = Execute | Write,
|
||||
ExecuteReadWrite = Execute | Read | Write,
|
||||
};
|
||||
|
||||
inline bool IsExecute(Mode mode)
|
||||
{
|
||||
return (mode == Mode::Execute || mode == Mode::ExecuteRead || mode == Mode::ExecuteWrite || mode == Mode::ExecuteReadWrite);
|
||||
}
|
||||
|
||||
uint64_t Alloc(uint64_t address, uint64_t size, Mode mode);
|
||||
uint64_t AllocAligned(uint64_t address, uint64_t size, Mode mode, uint64_t alignment);
|
||||
bool Free(uint64_t address);
|
||||
bool Protect(uint64_t address, uint64_t size, Mode mode, Mode* old_mode = nullptr);
|
||||
bool FlushInstructionCache(uint64_t address, uint64_t size);
|
||||
bool PatchReplace(uint64_t vaddr, uint64_t value);
|
||||
|
||||
} // namespace VirtualMemory
|
||||
|
||||
} // namespace Kyty::Loader
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_VIRTUALMEMORY_H_ */
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
local cfg = {
|
||||
ScreenWidth = 1280;
|
||||
ScreenHeight = 720;
|
||||
Neo = true;
|
||||
VulkanValidationEnabled = true;
|
||||
ShaderValidationEnabled = true;
|
||||
ShaderOptimizationType = 'Performance'; -- None, Size, Performance
|
||||
ShaderLogDirection = 'File'; -- Silent, Console, File
|
||||
ShaderLogFolder = '_Shaders';
|
||||
CommandBufferDumpEnabled = true;
|
||||
CommandBufferDumpFolder = '_Buffers';
|
||||
PrintfDirection = 'Console'; -- Silent, Console, File
|
||||
PrintfOutputFile = '_kyty.txt';
|
||||
ProfilerDirection = 'None'; -- None, File, Network, FileAndNetwork
|
||||
ProfilerOutputFile = '_profile.prof';
|
||||
}
|
||||
|
||||
kyty_init(cfg);
|
||||
|
||||
kyty_mount('z:/dev/ps4/tests/01_Hello_8/', '/app0');
|
||||
|
||||
kyty_load_elf('/app0/main.elf');
|
||||
kyty_load_elf('/app0/sce_module/libc.prx', 0);
|
||||
kyty_load_elf('/app0/sce_module/libSceFios2.prx', 0);
|
||||
|
||||
kyty_load_symbols('libc_internal_1');
|
||||
kyty_load_symbols('libkernel_1');
|
||||
kyty_load_symbols('libVideoOut_1');
|
||||
kyty_load_symbols('libSysmodule_1');
|
||||
kyty_load_symbols('libDiscMap_1');
|
||||
kyty_load_symbols('libDebug_1');
|
||||
kyty_load_symbols('libGraphicsDriver_1');
|
||||
kyty_load_symbols('libUserService_1');
|
||||
kyty_load_symbols('libPad_1');
|
||||
|
||||
kyty_execute();
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
local cfg = {
|
||||
ScreenWidth = 1280;
|
||||
ScreenHeight = 720;
|
||||
Neo = true;
|
||||
VulkanValidationEnabled = true;
|
||||
ShaderValidationEnabled = true;
|
||||
ShaderOptimizationType = 'Performance'; -- None, Size, Performance
|
||||
ShaderLogDirection = 'File'; -- Silent, Console, File
|
||||
ShaderLogFolder = '_Shaders';
|
||||
CommandBufferDumpEnabled = true;
|
||||
CommandBufferDumpFolder = '_Buffers';
|
||||
PrintfDirection = 'Console'; -- Silent, Console, File
|
||||
PrintfOutputFile = '_kyty.txt';
|
||||
ProfilerDirection = 'None'; -- None, File, Network, FileAndNetwork
|
||||
ProfilerOutputFile = '_profile.prof';
|
||||
}
|
||||
|
||||
kyty_init(cfg);
|
||||
|
||||
kyty_mount('z:/dev/ps5/tests/01_Hello/Debug', '/app0');
|
||||
|
||||
kyty_load_elf('/app0/main.elf');
|
||||
kyty_load_elf('/app0/sce_module/libc.prx');
|
||||
|
||||
kyty_load_symbols('libc_internal_1');
|
||||
kyty_load_symbols('libkernel_1');
|
||||
kyty_load_symbols('libVideoOut_1');
|
||||
kyty_load_symbols('libSysmodule_1');
|
||||
kyty_load_symbols('libDiscMap_1');
|
||||
|
||||
--kyty_dbg_dump('_elf/');
|
||||
|
||||
kyty_execute();
|
||||
|
||||
|
||||
|
||||
@@ -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