Implemented guest-to-host function pointers (WIP)

Co-Authored-By: Skyth (Asilkan) <19259897+blueskythlikesclouds@users.noreply.github.com>
This commit is contained in:
Hyper 2024-11-09 14:56:17 +00:00
parent 0962560ec9
commit 8623f5d778
2 changed files with 152 additions and 26 deletions

View file

@ -918,23 +918,23 @@ static void ProcSetRenderState(const RenderCommand& cmd)
static const std::pair<GuestRenderState, void*> g_setRenderStateFunctions[] =
{
{ D3DRS_ZENABLE, GuestFunction<SetRenderState<D3DRS_ZENABLE>> },
{ D3DRS_ZWRITEENABLE, GuestFunction<SetRenderState<D3DRS_ZWRITEENABLE>> },
{ D3DRS_ALPHATESTENABLE, GuestFunction<SetRenderState<D3DRS_ALPHATESTENABLE>> },
{ D3DRS_SRCBLEND, GuestFunction<SetRenderState<D3DRS_SRCBLEND>> },
{ D3DRS_DESTBLEND, GuestFunction<SetRenderState<D3DRS_DESTBLEND>> },
{ D3DRS_CULLMODE, GuestFunction<SetRenderState<D3DRS_CULLMODE>> },
{ D3DRS_ZFUNC, GuestFunction<SetRenderState<D3DRS_ZFUNC>> },
{ D3DRS_ALPHAREF, GuestFunction<SetRenderState<D3DRS_ALPHAREF>> },
{ D3DRS_ALPHABLENDENABLE, GuestFunction<SetRenderState<D3DRS_ALPHABLENDENABLE>> },
{ D3DRS_BLENDOP, GuestFunction<SetRenderState<D3DRS_BLENDOP>> },
{ D3DRS_SCISSORTESTENABLE, GuestFunction<SetRenderState<D3DRS_SCISSORTESTENABLE>> },
{ D3DRS_SLOPESCALEDEPTHBIAS, GuestFunction<SetRenderState<D3DRS_SLOPESCALEDEPTHBIAS>> },
{ D3DRS_DEPTHBIAS, GuestFunction<SetRenderState<D3DRS_DEPTHBIAS>> },
{ D3DRS_SRCBLENDALPHA, GuestFunction<SetRenderState<D3DRS_SRCBLENDALPHA>> },
{ D3DRS_DESTBLENDALPHA, GuestFunction<SetRenderState<D3DRS_DESTBLENDALPHA>> },
{ D3DRS_BLENDOPALPHA, GuestFunction<SetRenderState<D3DRS_BLENDOPALPHA>> },
{ D3DRS_COLORWRITEENABLE, GuestFunction<SetRenderState<D3DRS_COLORWRITEENABLE>> }
{ D3DRS_ZENABLE, HostToGuestFunction<SetRenderState<D3DRS_ZENABLE>> },
{ D3DRS_ZWRITEENABLE, HostToGuestFunction<SetRenderState<D3DRS_ZWRITEENABLE>> },
{ D3DRS_ALPHATESTENABLE, HostToGuestFunction<SetRenderState<D3DRS_ALPHATESTENABLE>> },
{ D3DRS_SRCBLEND, HostToGuestFunction<SetRenderState<D3DRS_SRCBLEND>> },
{ D3DRS_DESTBLEND, HostToGuestFunction<SetRenderState<D3DRS_DESTBLEND>> },
{ D3DRS_CULLMODE, HostToGuestFunction<SetRenderState<D3DRS_CULLMODE>> },
{ D3DRS_ZFUNC, HostToGuestFunction<SetRenderState<D3DRS_ZFUNC>> },
{ D3DRS_ALPHAREF, HostToGuestFunction<SetRenderState<D3DRS_ALPHAREF>> },
{ D3DRS_ALPHABLENDENABLE, HostToGuestFunction<SetRenderState<D3DRS_ALPHABLENDENABLE>> },
{ D3DRS_BLENDOP, HostToGuestFunction<SetRenderState<D3DRS_BLENDOP>> },
{ D3DRS_SCISSORTESTENABLE, HostToGuestFunction<SetRenderState<D3DRS_SCISSORTESTENABLE>> },
{ D3DRS_SLOPESCALEDEPTHBIAS, HostToGuestFunction<SetRenderState<D3DRS_SLOPESCALEDEPTHBIAS>> },
{ D3DRS_DEPTHBIAS, HostToGuestFunction<SetRenderState<D3DRS_DEPTHBIAS>> },
{ D3DRS_SRCBLENDALPHA, HostToGuestFunction<SetRenderState<D3DRS_SRCBLENDALPHA>> },
{ D3DRS_DESTBLENDALPHA, HostToGuestFunction<SetRenderState<D3DRS_DESTBLENDALPHA>> },
{ D3DRS_BLENDOPALPHA, HostToGuestFunction<SetRenderState<D3DRS_BLENDOPALPHA>> },
{ D3DRS_COLORWRITEENABLE, HostToGuestFunction<SetRenderState<D3DRS_COLORWRITEENABLE>> }
};
static std::unique_ptr<RenderPipeline> g_resolveMsaaDepthPipelines[3];
@ -1214,7 +1214,7 @@ static uint32_t CreateDevice(uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,
memset(device, 0, sizeof(*device));
uint32_t functionOffset = 0x443344; // D3D
g_codeCache.Insert(functionOffset, reinterpret_cast<void*>(GuestFunction<SetRenderStateUnimplemented>));
g_codeCache.Insert(functionOffset, reinterpret_cast<void*>(HostToGuestFunction<SetRenderStateUnimplemented>));
for (size_t i = 0; i < _countof(device->setRenderStateFunctions); i++)
device->setRenderStateFunctions[i] = functionOffset;

View file

@ -2,6 +2,7 @@
#include <cpu/ppc_context.h>
#include <array>
#include "xbox.h"
#include "memory.h"
template <typename R, typename... T>
constexpr std::tuple<T...> function_args(R(*)(T...)) noexcept
@ -78,10 +79,54 @@ struct ArgTranslator
[[unlikely]] default: break;
}
// how did you end up here
// TODO: get value from stack.
return 0;
}
FORCEINLINE constexpr static void SetIntegerArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, uint64_t value) noexcept
{
if (arg <= 7)
{
switch (arg)
{
case 0: ctx.r3.u64 = value; return;
case 1: ctx.r4.u64 = value; return;
case 2: ctx.r5.u64 = value; return;
case 3: ctx.r6.u64 = value; return;
case 4: ctx.r7.u64 = value; return;
case 5: ctx.r8.u64 = value; return;
case 6: ctx.r9.u64 = value; return;
case 7: ctx.r10.u64 = value; return;
[[unlikely]] default: break;
}
}
assert(arg < 7 && "Pushing to stack memory is not yet supported.");
}
FORCEINLINE static void SetPrecisionArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, double value) noexcept
{
switch (arg)
{
case 0: ctx.f1.f64 = value; return;
case 1: ctx.f2.f64 = value; return;
case 2: ctx.f3.f64 = value; return;
case 3: ctx.f4.f64 = value; return;
case 4: ctx.f5.f64 = value; return;
case 5: ctx.f6.f64 = value; return;
case 6: ctx.f7.f64 = value; return;
case 7: ctx.f8.f64 = value; return;
case 8: ctx.f9.f64 = value; return;
case 9: ctx.f10.f64 = value; return;
case 10: ctx.f11.f64 = value; return;
case 11: ctx.f12.f64 = value; return;
case 12: ctx.f13.f64 = value; return;
[[unlikely]] default: break;
}
assert(arg < 12 && "Pushing to stack memory is not yet supported.");
}
template<typename T>
FORCEINLINE constexpr static std::enable_if_t<!std::is_pointer_v<T>, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept
{
@ -106,6 +151,31 @@ struct ArgTranslator
return reinterpret_cast<T>(base + static_cast<uint32_t>(v));
}
template<typename T>
FORCEINLINE constexpr static std::enable_if_t<!std::is_pointer_v<T>, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept
{
if constexpr (is_precise_v<T>)
{
SetPrecisionArgumentValue(ctx, base, idx, value);
}
else
{
SetIntegerArgumentValue(ctx, base, idx, value);
}
}
template<typename T>
FORCEINLINE constexpr static std::enable_if_t<std::is_pointer_v<T>, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept
{
const auto v = g_memory.MapVirtual(value);
if (!v)
{
return;
}
SetValue(ctx, base, idx, v);
}
};
struct Argument
@ -155,27 +225,42 @@ struct arg_ordinal_t
};
template<auto Func, int I = 0, typename ...TArgs>
FORCEINLINE void _translate_args(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
requires (I >= sizeof...(TArgs))
FORCEINLINE void _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
requires (I >= sizeof...(TArgs))
{
}
template <auto Func, int I = 0, typename ...TArgs>
FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
{
using T = std::tuple_element_t<I, std::remove_reference_t<decltype(tpl)>>;
std::get<I>(tpl) = ArgTranslator::GetValue<T>(ctx, base, arg_ordinal_t<Func, I>::value);
_translate_args<Func, I + 1>(ctx, base, tpl);
_translate_args_to_host<Func, I + 1>(ctx, base, tpl);
}
template<auto Func, int I = 0, typename ...TArgs>
FORCEINLINE void _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
requires (I >= sizeof...(TArgs))
{
}
template <auto Func, int I = 0, typename ...TArgs>
FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
{
using T = std::tuple_element_t<I, std::remove_reference_t<decltype(tpl)>>;
ArgTranslator::SetValue<T>(ctx, base, I, std::get<I>(tpl));
_translate_args_to_guest<Func, I + 1>(ctx, base, tpl);
}
template<auto Func>
FORCEINLINE PPC_FUNC(GuestFunction)
FORCEINLINE PPC_FUNC(HostToGuestFunction)
{
using ret_t = decltype(std::apply(Func, function_args(Func)));
auto args = function_args(Func);
_translate_args<Func>(ctx, base, args);
_translate_args_to_host<Func>(ctx, base, args);
if constexpr (std::is_same_v<ret_t, void>)
{
@ -207,8 +292,49 @@ FORCEINLINE PPC_FUNC(GuestFunction)
}
}
template<typename T, typename... TArgs>
FORCEINLINE T GuestToHostFunction(uint32_t addr, TArgs... argv)
{
auto args = std::make_tuple(argv...);
auto& currentCtx = *GetPPCContext();
auto newCtx = PPCContext{};
newCtx.fn = currentCtx.fn;
newCtx.r1 = currentCtx.r1;
newCtx.r13 = currentCtx.r13;
newCtx.fpscr = currentCtx.fpscr;
_translate_args_to_guest<GuestToHostFunction<T, TArgs...>>(newCtx, (uint8_t*)g_memory.base, args);
SetPPCContext(newCtx);
(*(PPCFunc**)(newCtx.fn + uint64_t(addr) * 2))(newCtx, (uint8_t*)g_memory.base);
currentCtx.fpscr = newCtx.fpscr;
SetPPCContext(currentCtx);
if constexpr (std::is_void_v<T>)
{
return;
}
else if constexpr (std::is_pointer_v<T>)
{
return static_cast<T>((uint64_t)g_memory.Translate(newCtx.r3.u32));
}
else if constexpr (is_precise_v<T>)
{
return static_cast<T>(newCtx.f1.f64);
}
else if constexpr (std::is_integral_v<T>)
{
return static_cast<T>(newCtx.r3.u64);
}
else
{
static_assert(false, "Unsupported return type.");
}
}
#define GUEST_FUNCTION_HOOK(subroutine, function) \
PPC_FUNC(subroutine) { GuestFunction<function>(ctx, base); }
PPC_FUNC(subroutine) { HostToGuestFunction<function>(ctx, base); }
#define GUEST_FUNCTION_STUB(subroutine) \
PPC_FUNC(subroutine) { }