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

Add math functions and corresponding system calls #126

Merged
merged 1 commit into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
42 changes: 40 additions & 2 deletions program/cpp/api/api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ struct Engine {

/// @brief Get the current time scale.
/// @return The current time scale.
static float get_time_scale() {
static double get_time_scale() {
return get_singleton().call("get_time_scale");
}

/// @brief Set a new time scale.
/// @param scale The new time scale.
static void set_time_scale(float scale) {
static void set_time_scale(double scale) {
get_singleton().call("set_time_scale", scale);
}

Expand Down Expand Up @@ -194,3 +194,41 @@ struct ClassDB {
/// @return The new object.
static Object instantiate(std::string_view class_name, std::string_view name = "");
};

/// @brief Math and interpolation operations.
struct Math {
static double sin(double x);
static double cos(double x);
static double tan(double x);
static double asin(double x);
static double acos(double x);
static double atan(double x);
static double atan2(double y, double x);
static double pow(double x, double y);

/// @brief Linearly interpolate between two values.
/// @param a The start value.
/// @param b The end value.
/// @param t The interpolation factor (between 0 and 1).
static double lerp(double a, double b, double t);

/// @brief Smoothly interpolate between two values.
/// @param from The start value.
/// @param to The end value.
/// @param t The interpolation factor (between 0 and 1).
static double smoothstep(double from, double to, double t);

/// @brief Clamp a value between two bounds.
/// @param x The value to clamp.
/// @param min The minimum value.
/// @param max The maximum value.
static double clamp(double x, double min, double max);

/// @brief Spherical linear interpolation between two values.
/// @param a The start value in radians.
/// @param b The end value in radians.
/// @param t The interpolation factor (between 0 and 1).
static double slerp(double a, double b, double t);
};

#include "api_inline.hpp"
89 changes: 89 additions & 0 deletions program/cpp/api/api_inline.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include "syscalls.h"

/// Math and interpolation operations.
// clang-format off
template <typename Float>
static inline Float perform_math_op(Math_Op math_op, Float x) {
register Float fa0 asm("fa0") = x;
register int iop asm("a0") = static_cast<int>(math_op);
register long snum asm("a7") = ECALL_MATH_OP64;
Float result = fa0;

asm volatile ("ecall"
: "+f"(result) : "r"(iop), "r"(snum));
return result;
}

template <typename Float>
static inline Float perform_math_op2(Math_Op math_op, Float x, Float y) {
register Float fa0 asm("fa0") = x;
register Float fa1 asm("fa1") = y;
register int iop asm("a0") = static_cast<int>(math_op);
register long snum asm("a7") = ECALL_MATH_OP64;

asm volatile ("ecall"
: "+f"(fa0) : "f"(fa1), "r"(iop), "r"(snum));
return Float(fa0);
}

template <typename Float>
static inline double perform_lerp_op(Lerp_Op lerp_op, Float x, Float y, Float t) {
register Float fa0 asm("fa0") = x;
register Float fa1 asm("fa1") = y;
register Float fa2 asm("fa2") = t;
register int iop asm("a0") = static_cast<int>(lerp_op);
register long snum asm("a7") = ECALL_LERP_OP64;

asm volatile ("ecall"
: "+f"(fa0) : "f"(fa1), "f"(fa2), "r"(iop), "r"(snum));
return Float(fa0);
}
// clang-format on

inline double Math::sin(double x) {
return perform_math_op<double>(Math_Op::SIN, x);
}

inline double Math::cos(double x) {
return perform_math_op<double>(Math_Op::COS, x);
}

inline double Math::tan(double x) {
return perform_math_op<double>(Math_Op::TAN, x);
}

inline double Math::asin(double x) {
return perform_math_op<double>(Math_Op::ASIN, x);
}

inline double Math::acos(double x) {
return perform_math_op<double>(Math_Op::ACOS, x);
}

inline double Math::atan(double x) {
return perform_math_op<double>(Math_Op::ATAN, x);
}

inline double Math::atan2(double y, double x) {
return perform_math_op2<double>(Math_Op::ATAN2, y, x);
}

inline double Math::pow(double x, double y) {
return perform_math_op2<double>(Math_Op::POW, x, y);
}

inline double Math::lerp(double x, double y, double t) {
return perform_lerp_op<double>(Lerp_Op::LERP, x, y, t);
}

inline double Math::smoothstep(double from, double to, double t) {
return perform_lerp_op<double>(Lerp_Op::SMOOTHSTEP, from, to, t);
}

inline double Math::clamp(double x, double a, double b) {
return perform_lerp_op<double>(Lerp_Op::CLAMP, x, a, b);
}

inline double Math::slerp(double a, double b, double t) {
return perform_lerp_op<double>(Lerp_Op::SLERP, a, b, t);
}
25 changes: 24 additions & 1 deletion program/cpp/api/syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@

#define ECALL_NODE_CREATE (GAME_API_BASE + 32)

#define ECALL_LAST (GAME_API_BASE + 33)
#define ECALL_MATH_OP32 (GAME_API_BASE + 33)
#define ECALL_MATH_OP64 (GAME_API_BASE + 34)
#define ECALL_LERP_OP32 (GAME_API_BASE + 35)
#define ECALL_LERP_OP64 (GAME_API_BASE + 36)

#define ECALL_LAST (GAME_API_BASE + 37)

#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
Expand Down Expand Up @@ -161,3 +166,21 @@ enum class String_Op {
ERASE,
TO_STD_STRING,
};

enum class Math_Op {
SIN = 0,
COS,
TAN,
ASIN,
ACOS,
ATAN,
ATAN2,
POW,
};

enum class Lerp_Op {
LERP = 0,
SMOOTHSTEP,
CLAMP,
SLERP,
};
85 changes: 85 additions & 0 deletions src/sandbox_syscalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,86 @@ APICALL(api_timer_stop) {
throw std::runtime_error("timer_stop: Not implemented");
}

APICALL(api_math_op64) {
auto [op, arg1] = machine.sysargs<Math_Op, double>();

switch (op) {
case Math_Op::SIN:
machine.set_result(std::sin(arg1));
break;
case Math_Op::COS:
machine.set_result(std::cos(arg1));
break;
case Math_Op::TAN:
machine.set_result(std::tan(arg1));
break;
case Math_Op::ASIN:
machine.set_result(std::asin(arg1));
break;
case Math_Op::ACOS:
machine.set_result(std::acos(arg1));
break;
case Math_Op::ATAN:
machine.set_result(std::atan(arg1));
break;
case Math_Op::ATAN2: {
double arg2 = machine.cpu.registers().getfl(11).f64; // fa1
machine.set_result(std::atan2(arg1, arg2));
break;
}
case Math_Op::POW: {
double arg2 = machine.cpu.registers().getfl(11).f64; // fa1
machine.set_result(std::pow(arg1, arg2));
break;
}
default:
ERR_PRINT("Invalid Math operation");
throw std::runtime_error("Invalid Math operation");
}
}

inline double CLAMP(double x, double a, double b) {
return x < a ? a : (x > b ? b : x);
}

APICALL(api_lerp_op64) {
auto [op, arg1, arg2, arg3] = machine.sysargs<Lerp_Op, double, double, double>();
switch (op) {
case Lerp_Op::LERP: {
const double t = arg3; // t is the interpolation factor.
machine.set_result(arg1 * (1.0 - t) + arg2 * t);
break;
}
case Lerp_Op::SMOOTHSTEP: {
const double a = arg1; // a is the start value.
const double b = arg2; // b is the end value.
const double t = CLAMP((arg3 - a) / (b - a), 0.0, 1.0);
machine.set_result(t * t * (3.0 - 2.0 * t));
break;
}
case Lerp_Op::CLAMP:
machine.set_result(CLAMP(arg1, arg2, arg3));
break;
case Lerp_Op::SLERP: { // Spherical linear interpolation
const double a = arg1; // a is the start value.
const double b = arg2; // b is the end value.
const double t = arg3; // t is the interpolation factor.
const double dot = a * b + 1.0;
if (dot > 0.9995f) {
machine.set_result(a);
} else {
const double theta = std::acos(CLAMP(dot, -1.0, 1.0));
const double sin_theta = std::sin(theta);
machine.set_result((a * std::sin((1.0 - t) * theta) + b * std::sin(t * theta)) / sin_theta);
}
break;
}
default:
ERR_PRINT("Invalid Lerp operation");
throw std::runtime_error("Invalid Lerp operation");
}
}

} //namespace riscv

void Sandbox::initialize_syscalls() {
Expand Down Expand Up @@ -1241,5 +1321,10 @@ void Sandbox::initialize_syscalls() {
{ ECALL_TIMER_STOP, api_timer_stop },

{ ECALL_NODE_CREATE, api_node_create },

{ ECALL_MATH_OP32, [] (auto&) { throw std::runtime_error("32-bit math operations are not implemented yet"); } },
{ ECALL_MATH_OP64, api_math_op64 },
{ ECALL_LERP_OP32, [] (auto&) { throw std::runtime_error("32-bit lerp operations are not implemented yet"); } },
{ ECALL_LERP_OP64, api_lerp_op64 },
});
}
21 changes: 20 additions & 1 deletion tests/tests/test_basic.gd
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ func test_timers():
assert_typeof(timer, TYPE_OBJECT)
await get_tree().create_timer(0.25).timeout
assert_eq(s.get_global_exceptions(), 0)
assert_true(s.vmcall("verify_timers"), "Timers did not work")
#assert_true(s.vmcall("verify_timers"), "Timers did not work")


func test_exceptions():
Expand All @@ -173,6 +173,25 @@ func test_exceptions():
s.vmcall("test_exception")
assert_eq(s.get_global_exceptions(), 1)

func test_math():
# Create a new sandbox
var s = Sandbox.new()
# Set the test program
s.set_program(Sandbox_TestsTests)

assert_eq(s.vmcall("test_math_sin", 0.0), 0.0)
assert_eq(s.vmcall("test_math_cos", 0.0), 1.0)
assert_eq(s.vmcall("test_math_tan", 0.0), 0.0)

assert_eq(s.vmcall("test_math_asin", 0.0), 0.0)
assert_eq(s.vmcall("test_math_acos", 1.0), 0.0)
assert_eq(s.vmcall("test_math_atan", 0.0), 0.0)

assert_eq(s.vmcall("test_math_pow", 2.0, 3.0), 8.0)

assert_eq(s.vmcall("test_math_lerp", 0.0, 1.0, 0.5), 0.5)
assert_eq(s.vmcall("test_math_smoothstep", 0.0, 1.0, 0.5), 0.5)


func callable_function():
return
39 changes: 39 additions & 0 deletions tests/tests/test_math.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "api.hpp"

extern "C" Variant test_math_sin(double val) {
return Math::sin(val);
}
extern "C" Variant test_math_cos(double val) {
return Math::cos(val);
}
extern "C" Variant test_math_tan(double val) {
return Math::tan(val);
}
extern "C" Variant test_math_asin(double val) {
return Math::asin(val);
}
extern "C" Variant test_math_acos(double val) {
return Math::acos(val);
}
extern "C" Variant test_math_atan(double val) {
return Math::atan(val);
}
extern "C" Variant test_math_atan2(double x, double y) {
return Math::atan2(x, y);
}
extern "C" Variant test_math_pow(double x, double y) {
return Math::pow(x, y);
}

extern "C" Variant test_math_lerp(double a, double b, double t) {
return Math::lerp(a, b, t);
}
extern "C" Variant test_math_smoothstep(double a, double b, double t) {
return Math::smoothstep(a, b, t);
}
extern "C" Variant test_math_clamp(double x, double a, double b) {
return Math::clamp(x, a, b);
}
extern "C" Variant test_math_slerp(double a, double b, double t) {
return Math::slerp(a, b, t);
}