Skip to content

Commit

Permalink
SDK: Reduce heap allocation on invoke calls
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Apr 21, 2024
1 parent 6f66b80 commit 9732b4b
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 34 deletions.
16 changes: 16 additions & 0 deletions include/reframework/API.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extern "C" {
#include "API.h"
}

#include <span>
#include <mutex>
#include <array>
#include <vector>
Expand Down Expand Up @@ -516,6 +517,21 @@ class API {

auto result = fn(*this, obj, (void**)&args[0], args.size() * sizeof(void*), &out, sizeof(out));

#ifdef REFRAMEWORK_API_EXCEPTIONS
if (result != REFRAMEWORK_ERROR_NONE) {
throw std::runtime_error("Method invocation failed");
}
#endif

return out;
}

reframework::InvokeRet invoke(API::ManagedObject* obj, const std::span<void*>& args) {
static const auto fn = API::s_instance->sdk()->method->invoke;
reframework::InvokeRet out{};

auto result = fn(*this, obj, args.data(), args.size() * sizeof(void*), &out, sizeof(out));

#ifdef REFRAMEWORK_API_EXCEPTIONS
if (result != REFRAMEWORK_ERROR_NONE) {
throw std::runtime_error("Method invocation failed");
Expand Down
51 changes: 33 additions & 18 deletions shared/sdk/RETypeDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,30 @@ RETypeDB* RETypeDB::get() {
static std::shared_mutex g_tdb_type_mtx{};
static std::unordered_map<std::string, sdk::RETypeDefinition*> g_tdb_type_map{};

reframework::InvokeRet invoke_object_func(void* obj, sdk::RETypeDefinition* t, std::string_view name, const std::vector<void*>& args) {
reframework::InvokeRet invoke_object_func(void* obj, sdk::RETypeDefinition* t, std::string_view name, std::vector<void*>& args) {
const auto method = t->get_method(name);

if (method == nullptr) {
return reframework::InvokeRet{};
}

return method->invoke(obj, args);
return method->invoke(obj, std::span<void*>(args));
}

reframework::InvokeRet invoke_object_func(::REManagedObject* obj, std::string_view name, const std::vector<void*>& args) {
return invoke_object_func((void*)obj, utility::re_managed_object::get_type_definition(obj), name, args);
const auto t = utility::re_managed_object::get_type_definition(obj);

if (t == nullptr) {
return reframework::InvokeRet{};
}

const auto method = t->get_method(name);

if (method == nullptr) {
return reframework::InvokeRet{};
}

return method->invoke(obj, std::span<void*>(*const_cast<std::vector<void*>*>(&args)));
}

sdk::RETypeDefinition* RETypeDB::find_type(std::string_view name) const {
Expand Down Expand Up @@ -492,15 +504,22 @@ uint32_t sdk::REMethodDefinition::get_invoke_id() const {
return invoke_id;
}

reframework::InvokeRet sdk::REMethodDefinition::invoke(void* object, const std::vector<void*>& args) const {
reframework::InvokeRet sdk::REMethodDefinition::invoke(void* object, const std::span<void*>& args) const {
::reframework::InvokeRet out{};
invoke(object, args, out);

return out;
}

void sdk::REMethodDefinition::invoke(void* object, const std::span<void*>& args, ::reframework::InvokeRet& out) const {
const auto num_params = get_num_params();

if (num_params != args.size()) {
//throw std::runtime_error("Invalid number of arguments");
const auto declaring_type = get_declaring_type();
const auto decltype_name = declaring_type != nullptr ? declaring_type->get_full_name() : "unknownclass";
spdlog::warn("Invalid number of arguments passed to REMethodDefinition::invoke for {}.{}", decltype_name, get_name());
return reframework::InvokeRet{};
return;
}

#if TDB_VER > 49
Expand All @@ -516,8 +535,6 @@ reframework::InvokeRet sdk::REMethodDefinition::invoke(void* object, const std::
void* object_ptr; //0x0040 aka "this" pointer
};

reframework::InvokeRet out{};

StackFrame stack_frame{};
stack_frame.method = this;
stack_frame.object_ptr = object;
Expand Down Expand Up @@ -591,28 +608,28 @@ reframework::InvokeRet sdk::REMethodDefinition::invoke(void* object, const std::

out.exception_thrown = true;

return out;
return;
}
}

if (stack_frame.out_data != &out) {
out.ptr = stack_frame.out_data;
return out;
return;
}

return out;
return;
#else
// RE7 doesn't have the invoke wrappers that the newer games use...
if (num_params > 3) {
spdlog::warn("REMethodDefinition::invoke for {} has more than 2 parameters, which is not supported at this time (RE7)", get_name());
return reframework::InvokeRet{};
return;
}

const bool is_static = this->is_static();

if (!is_static && object == nullptr) {
spdlog::warn("REMethodDefinition::invoke for {} is not static, but object is nullptr", get_name());
return reframework::InvokeRet{};
return;
}

auto ret_ty = get_return_type();
Expand All @@ -631,8 +648,6 @@ reframework::InvokeRet sdk::REMethodDefinition::invoke(void* object, const std::
is_ptr = true;
}

reframework::InvokeRet out{};

const auto param_types = get_param_types();
std::vector<size_t> param_hashes{};
std::vector<void*> converted_args(args.size());
Expand Down Expand Up @@ -719,7 +734,7 @@ reframework::InvokeRet sdk::REMethodDefinition::invoke(void* object, const std::
switch (num_params) {
case 0:
unpack_and_call();
return out;
return;

break;
case 1:
Expand All @@ -732,7 +747,7 @@ reframework::InvokeRet sdk::REMethodDefinition::invoke(void* object, const std::
unpack_and_call.operator()<void*>();
}

return out;
return;

break;
case 2:
Expand All @@ -757,7 +772,7 @@ reframework::InvokeRet sdk::REMethodDefinition::invoke(void* object, const std::
unpack_and_call.operator()<void*, void*>();
}

return out;
return;

break;
case 3:
Expand Down Expand Up @@ -901,7 +916,7 @@ reframework::InvokeRet sdk::REMethodDefinition::invoke(void* object, const std::
break;
}

return out;
return;
#endif
}

Expand Down
16 changes: 15 additions & 1 deletion shared/sdk/RETypeDB.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <string_view>
#include <vector>
#include <span>
#include <cstdint>

// Forward decls
Expand Down Expand Up @@ -1140,7 +1141,20 @@ struct REMethodDefinition : public sdk::REMethodDefinition_ {
// calling is the actual call to the function
// invoking is calling a wrapper function that calls the function
// using an array of arguments
::reframework::InvokeRet invoke(void* object, const std::vector<void*>& args) const;
void invoke(void* object, const std::span<void*>& args, ::reframework::InvokeRet& out) const;
::reframework::InvokeRet invoke(void* object, const std::span<void*>& args) const;

template<size_t N>
::reframework::InvokeRet invoke(void* object, std::array<void*, N>& args) const {
return invoke(object, std::span<void*>(args));
}

template<typename... Args>
requires (std::is_same_v<Args, void*> && ...)
::reframework::InvokeRet invoke(void* object, Args... args) const {
std::array<void*, sizeof...(Args)> arg_array{args...};
return invoke(object, arg_array);
}

uint32_t get_invoke_id() const;
uint32_t get_num_params() const;
Expand Down
2 changes: 1 addition & 1 deletion shared/sdk/RETypeDefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ bool RETypeDefinition::has_attribute(::REManagedObject* attribute_runtime_type,
return false;
}

const auto res = get_custom_attributes->invoke(runtime_type, std::vector<void*>{attribute_runtime_type, (void*)(uint64_t)inherit});
const auto res = get_custom_attributes->invoke(runtime_type, (void*)attribute_runtime_type, (void*)(uint64_t)inherit);
const auto attributes_array = (sdk::SystemArray*)res.ptr;

if (attributes_array == nullptr) {
Expand Down
13 changes: 3 additions & 10 deletions src/mods/PluginLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,17 +307,10 @@ REFrameworkTDBMethod g_tdb_method_data {
return REFRAMEWORK_ERROR_IN_ARGS_SIZE_MISMATCH;
}

std::vector<void*> cpp_args{};
const auto arg_count = in_args_size / sizeof(void*);
m->invoke(thisptr, std::span<void*>(in_args, arg_count), *(reframework::InvokeRet*)out);

for (auto i = 0; i < in_args_size / sizeof(void*); i++) {
cpp_args.push_back(in_args[i]);
}

auto ret = m->invoke(thisptr, cpp_args);

memcpy(out, &ret, sizeof(reframework::InvokeRet));

if (ret.exception_thrown) {
if (((reframework::InvokeRet*)out)->exception_thrown) {
return REFRAMEWORK_ERROR_EXCEPTION;
}

Expand Down
3 changes: 2 additions & 1 deletion src/mods/bindings/Sdk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,8 @@ struct ValueType {
return sol::make_object(l, sol::nil);
}

auto ret_val = def->invoke(real_obj, ::api::sdk::build_args(va));
auto vec_args = ::api::sdk::build_args(va);
auto ret_val = def->invoke(real_obj, std::span(vec_args));

if (ret_val.exception_thrown) {
throw sol::error("Invoke threw an exception");
Expand Down
6 changes: 3 additions & 3 deletions src/mods/tools/ObjectExplorer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3492,12 +3492,12 @@ void ObjectExplorer::attempt_display_field(REManagedObject* obj, VariableDescrip
const auto should_pass_result_ptr = result_type != nullptr && result_type->is_value_type() && (result_type->get_valuetype_size() > sizeof(void*) || (!result_type->is_primitive() && !result_type->is_enum()));

if (result_type == nullptr) {
setter->invoke(obj, {dummy_data.ptr});
setter->invoke(obj, dummy_data.ptr);
} else {
if (should_pass_result_ptr) {
setter->invoke(obj, {dummy_data.bytes.data()});
setter->invoke(obj, (void*)dummy_data.bytes.data());
} else {
setter->invoke(obj, {dummy_data.ptr});
setter->invoke(obj, dummy_data.ptr);
}
}
}
Expand Down

0 comments on commit 9732b4b

Please sign in to comment.