Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Library Forwarding: Add support for 32-bit Vulkan #3487

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Library Forwarding: Add custom repacking tests for Vulkan-like scenarios
  • Loading branch information
neobrain committed Mar 7, 2024
commit 7da0e5f758764eda8bcc65e7349a14f2bb2f48bb
83 changes: 82 additions & 1 deletion ThunkLibs/libfex_thunk_test/Host.cpp
Original file line number Diff line number Diff line change
@@ -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,84 @@ bool fex_custom_repack_exit(guest_layout<CustomRepackedType>& to, host_layout<Cu
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)
33 changes: 33 additions & 0 deletions ThunkLibs/libfex_thunk_test/api.h
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
#pragma once

#include <cstdint>
#include <limits>

extern "C" {

@@ -74,4 +75,36 @@ 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);

}
27 changes: 27 additions & 0 deletions ThunkLibs/libfex_thunk_test/lib.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "api.h"

#include <cstdio>
#include <cstddef>

extern "C" {

uint32_t GetDoubledValue(uint32_t input) {
@@ -59,4 +62,28 @@ 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"
6 changes: 6 additions & 0 deletions ThunkLibs/libfex_thunk_test/libfex_thunk_test_interface.cpp
Original file line number Diff line number Diff line change
@@ -35,3 +35,9 @@ template<> struct fex_gen_config<&CustomRepackedType::data> : fexgen::custom_rep
template<> 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> {};
23 changes: 23 additions & 0 deletions unittests/FEXLinuxTests/tests/thunks/thunk_testlib.cpp
Original file line number Diff line number Diff line change
@@ -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);
}