mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 08:00:15 +02:00
LibraryForwarding: Add custom repacking tests for Vulkan-like scenarios
This commit is contained in:
1 parent
bdd351a42c
commit
585320093a
5 files changed
+174
-1
No files matched your search
@@ -4,9 +4,10 @@ tags: thunklibs|fex_thunk_test
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/Host.h"
|
||||
|
||||
#include "api.h"
|
||||
@@ -29,4 +30,86 @@ bool fex_custom_repack_exit(guest_layout<CustomRepackedType>& to, const host_lay
|
||||
return false;
|
||||
}
|
||||
|
||||
template<StructType TypeIndex, typename Type>
|
||||
static const TestBaseStruct* convert(const TestBaseStruct* source) {
|
||||
// Using malloc here since no easily available type information is available at the time of destruction.
|
||||
auto guest_next = reinterpret_cast<guest_layout<Type>*>((void*)source);
|
||||
auto child_mem = (char*)aligned_alloc(alignof(host_layout<Type>), sizeof(host_layout<Type>));
|
||||
auto child = new (child_mem) host_layout<Type> {*guest_next};
|
||||
|
||||
fex_custom_repack_entry(*child, *reinterpret_cast<guest_layout<Type>*>((void*)(source)));
|
||||
|
||||
return (const TestBaseStruct*)child;
|
||||
}
|
||||
|
||||
template<StructType TypeIndex, typename Type>
|
||||
static void convert_to_guest(void* into, const TestBaseStruct* from) {
|
||||
auto typed_into = (guest_layout<Type>*)into;
|
||||
auto oldNext = typed_into->data.Next;
|
||||
*typed_into = to_guest(to_host_layout(*(Type*)from));
|
||||
typed_into->data.Next = oldNext;
|
||||
|
||||
fex_custom_repack_exit(*typed_into, to_host_layout(*(Type*)from));
|
||||
}
|
||||
|
||||
template<StructType TypeIndex, typename Type>
|
||||
inline constexpr std::pair<StructType, std::pair<const TestBaseStruct* (*)(const TestBaseStruct*), void (*)(void*, const TestBaseStruct*)>> converters = {
|
||||
TypeIndex,
|
||||
{convert<TypeIndex, Type>, convert_to_guest<TypeIndex, Type>}};
|
||||
|
||||
static std::unordered_map<StructType, std::pair<const TestBaseStruct* (*)(const TestBaseStruct*), void (*)(void*, const TestBaseStruct*)>> next_handlers {
|
||||
converters<StructType::Struct1, TestStruct1>,
|
||||
converters<StructType::Struct2, TestStruct2>,
|
||||
};
|
||||
|
||||
static void default_fex_custom_repack_entry(TestBaseStruct& into, const guest_layout<TestBaseStruct>* from) {
|
||||
if (!from->data.Next.get_pointer()) {
|
||||
into.Next = nullptr;
|
||||
return;
|
||||
}
|
||||
auto typed_source = reinterpret_cast<const guest_layout<TestBaseStruct>*>(from->data.Next.get_pointer());
|
||||
|
||||
auto next_handler = next_handlers.at(StructType {typed_source->data.Type.data});
|
||||
|
||||
into.Next = (TestBaseStruct*)next_handler.first((const TestBaseStruct*)typed_source);
|
||||
}
|
||||
|
||||
static void default_fex_custom_repack_reverse(guest_layout<TestBaseStruct>& into, const TestBaseStruct* from) {
|
||||
auto NextHost = from->Next;
|
||||
if (!NextHost) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto next_handler = next_handlers.at(static_cast<StructType>(into.data.Next.get_pointer()->data.Type.data));
|
||||
next_handler.second((void*)into.data.Next.get_pointer(), from->Next);
|
||||
|
||||
free((void*)NextHost);
|
||||
}
|
||||
|
||||
#define CREATE_INFO_DEFAULT_CUSTOM_REPACK(name) \
|
||||
void fex_custom_repack_entry(host_layout<name>& into, const guest_layout<name>& from) { \
|
||||
default_fex_custom_repack_entry(*(TestBaseStruct*)&into.data, reinterpret_cast<const guest_layout<TestBaseStruct>*>(&from)); \
|
||||
} \
|
||||
\
|
||||
bool fex_custom_repack_exit(guest_layout<name>& into, const host_layout<name>& from) { \
|
||||
auto prev_next = into.data.Next; \
|
||||
default_fex_custom_repack_reverse(*reinterpret_cast<guest_layout<TestBaseStruct>*>(&into), \
|
||||
&reinterpret_cast<const TestBaseStruct&>(from.data)); \
|
||||
into = to_guest(from); \
|
||||
into.data.Next = prev_next; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
CREATE_INFO_DEFAULT_CUSTOM_REPACK(TestStruct1)
|
||||
CREATE_INFO_DEFAULT_CUSTOM_REPACK(TestStruct2)
|
||||
|
||||
void fex_custom_repack_entry(host_layout<TestBaseStruct>&, const guest_layout<TestBaseStruct>&) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
bool fex_custom_repack_exit(guest_layout<TestBaseStruct>&, const host_layout<TestBaseStruct>&) {
|
||||
std::abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
EXPORTS(libfex_thunk_test)
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -73,4 +74,36 @@ enum DivType : uint8_t {};
|
||||
enum DivType : uint32_t {};
|
||||
#endif
|
||||
int FunctionWithDivergentSignature(DivType, DivType, DivType, DivType);
|
||||
|
||||
|
||||
/// Interfaces used to test Vulkan-like APIs
|
||||
|
||||
// Equivalent of VkStructureType
|
||||
enum class StructType {
|
||||
Struct1,
|
||||
Struct2,
|
||||
};
|
||||
|
||||
// Equivalent of VkBaseInStructure
|
||||
struct TestBaseStruct {
|
||||
TestBaseStruct* Next;
|
||||
StructType Type;
|
||||
};
|
||||
|
||||
// Equivalent of e.g. VkImageCreateInfo
|
||||
struct TestStruct1 {
|
||||
const void* Next;
|
||||
StructType Type; // StructType::Struct1
|
||||
uint8_t Data2;
|
||||
uint8_t pad0[3];
|
||||
int Data1;
|
||||
};
|
||||
|
||||
struct TestStruct2 {
|
||||
const void* Next;
|
||||
StructType Type; // StructType::Struct2
|
||||
int Data1;
|
||||
};
|
||||
|
||||
int ReadData1(TestStruct1*, int depth);
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "api.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstddef>
|
||||
|
||||
extern "C" {
|
||||
|
||||
uint32_t GetDoubledValue(uint32_t input) {
|
||||
@@ -59,4 +62,25 @@ int FunctionWithDivergentSignature(DivType a, DivType b, DivType c, DivType d) {
|
||||
return ((uint8_t)a << 24) | ((uint8_t)b << 16) | ((uint8_t)c << 8) | (uint8_t)d;
|
||||
}
|
||||
|
||||
int ReadData1(TestStruct1* data, int depth) {
|
||||
auto* base = (TestBaseStruct*)data;
|
||||
for (int i = 0; i != depth; ++i) {
|
||||
if (!base) {
|
||||
return -1;
|
||||
}
|
||||
base = base->Next;
|
||||
}
|
||||
if (!base) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (base->Type) {
|
||||
case StructType::Struct1: return ((TestStruct1*)base)->Data1;
|
||||
|
||||
case StructType::Struct2: return ((TestStruct2*)base)->Data1;
|
||||
|
||||
default: return -2;
|
||||
}
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -53,3 +53,13 @@ struct fex_gen_config<RanCustomRepack> {};
|
||||
|
||||
template<>
|
||||
struct fex_gen_config<FunctionWithDivergentSignature> {};
|
||||
|
||||
template<>
|
||||
struct fex_gen_config<&TestBaseStruct::Next> : fexgen::custom_repack {};
|
||||
template<>
|
||||
struct fex_gen_config<&TestStruct1::Next> : fexgen::custom_repack {};
|
||||
template<>
|
||||
struct fex_gen_config<&TestStruct2::Next> : fexgen::custom_repack {};
|
||||
|
||||
template<>
|
||||
struct fex_gen_config<ReadData1> {};
|
||||
@@ -36,6 +36,8 @@ struct Fixture {
|
||||
GET_SYMBOL(RanCustomRepack);
|
||||
|
||||
GET_SYMBOL(FunctionWithDivergentSignature);
|
||||
|
||||
GET_SYMBOL(ReadData1);
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(Fixture, "Trivial") {
|
||||
@@ -89,3 +91,24 @@ TEST_CASE_METHOD(Fixture, "Assisted struct repacking") {
|
||||
TEST_CASE_METHOD(Fixture, "Function signature with differing parameter sizes") {
|
||||
CHECK(FunctionWithDivergentSignature(DivType {1}, DivType {2}, DivType {3}, DivType {4}) == 0x01020304);
|
||||
}
|
||||
|
||||
// Test Vulkan-like linked lists
|
||||
TEST_CASE_METHOD(Fixture, "Assisted repacking of linked lists") {
|
||||
const int s2_data = 0xcddeeff;
|
||||
TestStruct2 s2 {
|
||||
.Next = nullptr,
|
||||
.Type = StructType::Struct2,
|
||||
.Data1 = s2_data,
|
||||
};
|
||||
|
||||
const int s1_data = 0x1234567;
|
||||
TestStruct1 s1 {
|
||||
.Next = &s2,
|
||||
.Type = StructType::Struct1,
|
||||
.Data2 = 0xab,
|
||||
.Data1 = s1_data,
|
||||
};
|
||||
|
||||
CHECK(ReadData1(&s1, 0) == s1_data);
|
||||
CHECK(ReadData1(&s1, 1) == s2_data);
|
||||
}
|
||||
Reference in new issue
Block a user