Format ultramodern

This commit is contained in:
angie 2024-06-10 10:24:49 -04:00
parent 0b01a0e528
commit 108a240186
23 changed files with 452 additions and 453 deletions

View file

@ -1,8 +1,8 @@
#ifndef __CONFIG_HPP__
#define __CONFIG_HPP__
#include <string>
#include <optional>
#include <string>
#include "json/json.hpp"
@ -130,7 +130,7 @@ namespace ultramodern {
{ultramodern::renderer::HighPrecisionFramebuffer::Off, "Off"},
});
// clang-format on
}
}
} // namespace renderer
} // namespace ultramodern
#endif

View file

@ -8,7 +8,7 @@
namespace ultramodern {
namespace error_handling {
struct callbacks_t {
using message_box_t = void(const char* msg);
using message_box_t = void(const char *msg);
/**
* Show an OS dialog with the given `msg`.
@ -20,10 +20,10 @@ namespace ultramodern {
void set_callbacks(const callbacks_t& callbacks);
void message_box(const char* msg);
void message_box(const char *msg);
[[noreturn]] void quick_exit(const char* filename, int line, const char *func, int exit_status = EXIT_FAILURE);
}
}
[[noreturn]] void quick_exit(const char *filename, int line, const char *func, int exit_status = EXIT_FAILURE);
} // namespace error_handling
} // namespace ultramodern
#endif

View file

@ -10,16 +10,16 @@ namespace ultramodern {
/**
* Called in each VI.
*/
vi_callback_t* vi_callback;
vi_callback_t *vi_callback;
/**
* Called before entering the gfx main loop.
*/
gfx_init_callback_t* gfx_init_callback;
gfx_init_callback_t *gfx_init_callback;
};
void set_callbacks(const callbacks_t& callbacks);
}
}
} // namespace events
} // namespace ultramodern
#endif

View file

@ -26,11 +26,11 @@ namespace ultramodern {
struct callbacks_t {
using poll_input_t = void(void);
using get_input_t = bool(int controller_num, uint16_t* buttons, float* x, float* y);
using get_input_t = bool(int controller_num, uint16_t *buttons, float *x, float *y);
using set_rumble_t = void(int controller_num, bool rumble);
using get_connected_device_info_t = connected_device_info_t(int controller_num);
poll_input_t* poll_input;
poll_input_t *poll_input;
/**
* Requests the state of the pressed buttons and the analog stick for the given `controller_num`.
@ -39,25 +39,25 @@ namespace ultramodern {
*
* Returns `true` if was able to fetch the specified data, `false` otherwise and the parameter arguments are left untouched.
*/
get_input_t* get_input;
get_input_t *get_input;
/**
* Turns on or off rumbling for the specified controller.
*
* `controller_num` is zero-indexed, meaning 0 corresponds to the first controller.
*/
set_rumble_t* set_rumble;
set_rumble_t *set_rumble;
/**
* Returns the connected device info for the given `controller_num` (as in, the controller port of the console).
*
* `controller_num` is zero-indexed, meaning 0 corresponds to the first controller.
*/
get_connected_device_info_t* get_connected_device_info;
get_connected_device_info_t *get_connected_device_info;
};
void set_callbacks(const callbacks_t& callbacks);
}
}
} // namespace input
} // namespace ultramodern
#endif

View file

@ -7,21 +7,21 @@
#include <span>
#if defined(_WIN32)
# define WIN32_LEAN_AND_MEAN
# include <Windows.h>
# define WIN32_LEAN_AND_MEAN
# include <Windows.h>
#elif defined(__ANDROID__)
# include "android/native_window.h"
# include "android/native_window.h"
#elif defined(__linux__)
# include "X11/Xlib.h"
# undef None
# undef Status
# undef LockMask
# undef Always
# undef Success
# include "X11/Xlib.h"
# undef None
# undef Status
# undef LockMask
# undef Always
# undef Success
#endif
#include "ultra64.h"
#include "config.hpp"
#include "ultra64.h"
namespace ultramodern {
namespace renderer {
@ -34,17 +34,17 @@ namespace ultramodern {
auto operator<=>(const WindowHandle&) const = default;
};
#elif defined(__ANDROID__)
using WindowHandle = ANativeWindow*;
using WindowHandle = ANativeWindow *;
#elif defined(__linux__)
struct WindowHandle {
Display* display;
Display *display;
Window window;
auto operator<=>(const WindowHandle&) const = default;
};
#elif defined(__APPLE__)
struct WindowHandle {
void* window;
void* view;
void *window;
void *view;
auto operator<=>(const WindowHandle&) const = default;
};
#endif
@ -58,28 +58,31 @@ namespace ultramodern {
};
class RendererContext {
public:
virtual ~RendererContext() = default;
public:
virtual ~RendererContext() = default;
virtual bool valid() = 0;
virtual SetupResult get_setup_result() const { return setup_result; }
virtual bool valid() = 0;
virtual SetupResult get_setup_result() const {
return setup_result;
}
virtual bool update_config(const GraphicsConfig& old_config, const GraphicsConfig& new_config) = 0;
virtual bool update_config(const GraphicsConfig& old_config, const GraphicsConfig& new_config) = 0;
virtual void enable_instant_present() = 0;
virtual void send_dl(const OSTask* task) = 0;
virtual void update_screen(uint32_t vi_origin) = 0;
virtual void shutdown() = 0;
virtual uint32_t get_display_framerate() const = 0;
virtual float get_resolution_scale() const = 0;
virtual void load_shader_cache(std::span<const char> cache_binary) = 0;
virtual void enable_instant_present() = 0;
virtual void send_dl(const OSTask *task) = 0;
virtual void update_screen(uint32_t vi_origin) = 0;
virtual void shutdown() = 0;
virtual uint32_t get_display_framerate() const = 0;
virtual float get_resolution_scale() const = 0;
virtual void load_shader_cache(std::span<const char> cache_binary) = 0;
protected:
SetupResult setup_result;
protected:
SetupResult setup_result;
};
struct callbacks_t {
using create_render_context_t = std::unique_ptr<RendererContext>(uint8_t* rdram, WindowHandle window_handle, bool developer_mode);
using create_render_context_t =
std::unique_ptr<RendererContext>(uint8_t *rdram, WindowHandle window_handle, bool developer_mode);
using get_graphics_api_name_t = std::string(const GraphicsConfig& config);
/**
@ -97,10 +100,10 @@ namespace ultramodern {
void set_callbacks(const callbacks_t& callbacks);
std::unique_ptr<RendererContext> create_render_context(uint8_t* rdram, WindowHandle window_handle, bool developer_mode);
std::unique_ptr<RendererContext> create_render_context(uint8_t *rdram, WindowHandle window_handle, bool developer_mode);
std::string get_graphics_api_name(const GraphicsConfig& config);
}
}
} // namespace renderer
} // namespace ultramodern
#endif

View file

@ -13,23 +13,23 @@ namespace ultramodern {
namespace rsp {
struct callbacks_t {
using init_t = void();
using run_microcode_t = bool(RDRAM_ARG const OSTask* task);
using run_microcode_t = bool(RDRAM_ARG const OSTask *task);
init_t* init;
init_t *init;
/**
* Executes the given RSP task.
*
* Returns true if task was executed successfully.
*/
run_microcode_t* run_task;
run_microcode_t *run_task;
};
void set_callbacks(const callbacks_t& callbacks);
void init();
bool run_task(RDRAM_ARG const OSTask* task);
};
bool run_task(RDRAM_ARG const OSTask *task);
}; // namespace rsp
} // namespace ultramodern
#endif

View file

@ -4,11 +4,11 @@
#include <stdint.h>
#ifdef __GNUC__
#define UNUSED __attribute__((unused))
#define ALIGNED(x) __attribute__((aligned(x)))
# define UNUSED __attribute__((unused))
# define ALIGNED(x) __attribute__((aligned(x)))
#else
#define UNUSED
#define ALIGNED(x)
# define UNUSED
# define ALIGNED(x)
#endif
typedef int64_t s64;
@ -23,60 +23,60 @@ typedef uint8_t u8;
// TODO allow a compile-time flag to be set to switch between recomp mode and
// fully native mode.
#if 0 // For native compilation
# define PTR(x) x*
# define RDRAM_ARG
# define RDRAM_ARG1
# define PASS_RDRAM
# define PASS_RDRAM1
# define TO_PTR(type, var) var
# define GET_MEMBER(type, addr, member) (&addr->member)
# ifdef __cplusplus
# define NULLPTR nullptr
# endif
# define PTR(x) x *
# define RDRAM_ARG
# define RDRAM_ARG1
# define PASS_RDRAM
# define PASS_RDRAM1
# define TO_PTR(type, var) var
# define GET_MEMBER(type, addr, member) (&addr->member)
# ifdef __cplusplus
# define NULLPTR nullptr
# endif
#else
# define PTR(x) int32_t
# define RDRAM_ARG uint8_t *rdram,
# define RDRAM_ARG1 uint8_t *rdram
# define PASS_RDRAM rdram,
# define PASS_RDRAM1 rdram
# define TO_PTR(type, var) ((type*)(&rdram[(uint64_t)var - 0xFFFFFFFF80000000]))
# define GET_MEMBER(type, addr, member) (addr + (intptr_t)&(((type*)nullptr)->member))
# ifdef __cplusplus
# define NULLPTR (PTR(void))0
# endif
# define PTR(x) int32_t
# define RDRAM_ARG uint8_t *rdram,
# define RDRAM_ARG1 uint8_t *rdram
# define PASS_RDRAM rdram,
# define PASS_RDRAM1 rdram
# define TO_PTR(type, var) ((type *)(&rdram[(uint64_t)var - 0xFFFFFFFF80000000]))
# define GET_MEMBER(type, addr, member) (addr + (intptr_t) & (((type *)nullptr)->member))
# ifdef __cplusplus
# define NULLPTR (PTR(void))0
# endif
#endif
#ifndef NULL
#define NULL (PTR(void) 0)
# define NULL (PTR(void) 0)
#endif
#define OS_MESG_NOBLOCK 0
#define OS_MESG_BLOCK 1
#define OS_MESG_NOBLOCK 0
#define OS_MESG_BLOCK 1
typedef s32 OSPri;
typedef s32 OSId;
typedef u64 OSTime;
typedef u64 OSTime;
#define OS_EVENT_SW1 0 /* CPU SW1 interrupt */
#define OS_EVENT_SW2 1 /* CPU SW2 interrupt */
#define OS_EVENT_CART 2 /* Cartridge interrupt: used by rmon */
#define OS_EVENT_COUNTER 3 /* Counter int: used by VI/Timer Mgr */
#define OS_EVENT_SP 4 /* SP task done interrupt */
#define OS_EVENT_SI 5 /* SI (controller) interrupt */
#define OS_EVENT_AI 6 /* AI interrupt */
#define OS_EVENT_VI 7 /* VI interrupt: used by VI/Timer Mgr */
#define OS_EVENT_PI 8 /* PI interrupt: used by PI Manager */
#define OS_EVENT_DP 9 /* DP full sync interrupt */
#define OS_EVENT_CPU_BREAK 10 /* CPU breakpoint: used by rmon */
#define OS_EVENT_SP_BREAK 11 /* SP breakpoint: used by rmon */
#define OS_EVENT_FAULT 12 /* CPU fault event: used by rmon */
#define OS_EVENT_THREADSTATUS 13 /* CPU thread status: used by rmon */
#define OS_EVENT_PRENMI 14 /* Pre NMI interrupt */
#define OS_EVENT_SW1 0 /* CPU SW1 interrupt */
#define OS_EVENT_SW2 1 /* CPU SW2 interrupt */
#define OS_EVENT_CART 2 /* Cartridge interrupt: used by rmon */
#define OS_EVENT_COUNTER 3 /* Counter int: used by VI/Timer Mgr */
#define OS_EVENT_SP 4 /* SP task done interrupt */
#define OS_EVENT_SI 5 /* SI (controller) interrupt */
#define OS_EVENT_AI 6 /* AI interrupt */
#define OS_EVENT_VI 7 /* VI interrupt: used by VI/Timer Mgr */
#define OS_EVENT_PI 8 /* PI interrupt: used by PI Manager */
#define OS_EVENT_DP 9 /* DP full sync interrupt */
#define OS_EVENT_CPU_BREAK 10 /* CPU breakpoint: used by rmon */
#define OS_EVENT_SP_BREAK 11 /* SP breakpoint: used by rmon */
#define OS_EVENT_FAULT 12 /* CPU fault event: used by rmon */
#define OS_EVENT_THREADSTATUS 13 /* CPU thread status: used by rmon */
#define OS_EVENT_PRENMI 14 /* Pre NMI interrupt */
#define M_GFXTASK 1
#define M_AUDTASK 2
#define M_VIDTASK 3
#define M_GFXTASK 1
#define M_AUDTASK 2
#define M_VIDTASK 3
#define M_NJPEGTASK 4
/////////////
@ -103,7 +103,7 @@ typedef struct OSThread_t {
uint16_t state;
OSId id;
int32_t pad3;
UltraThreadContext* context; // An actual pointer regardless of platform
UltraThreadContext *context; // An actual pointer regardless of platform
int32_t sp;
} OSThread;
@ -112,7 +112,7 @@ typedef PTR(void) OSMesg;
typedef struct OSMesgQueue {
PTR(OSThread) blocked_on_recv; /* Linked list of threads blocked on receiving from this queue */
PTR(OSThread) blocked_on_send; /* Linked list of threads blocked on sending to this queue */
PTR(OSThread) blocked_on_send; /* Linked list of threads blocked on sending to this queue */
s32 validCount; /* Number of messages in the queue */
s32 first; /* Index of the first message in the ring buffer */
s32 msgCount; /* Size of message buffer */
@ -122,29 +122,29 @@ typedef struct OSMesgQueue {
// RSP
typedef struct {
u32 type;
u32 flags;
u32 type;
u32 flags;
PTR(u64) ucode_boot;
u32 ucode_boot_size;
u32 ucode_boot_size;
PTR(u64) ucode;
u32 ucode_size;
u32 ucode_size;
PTR(u64) ucode_data;
u32 ucode_data_size;
u32 ucode_data_size;
PTR(u64) dram_stack;
u32 dram_stack_size;
u32 dram_stack_size;
PTR(u64) output_buff;
PTR(u64) output_buff_size;
PTR(u64) data_ptr;
u32 data_size;
u32 data_size;
PTR(u64) yield_data_ptr;
u32 yield_data_size;
u32 yield_data_size;
} OSTask_s;
typedef union {
@ -163,55 +163,55 @@ struct OSIoMesgHdr {
};
struct OSIoMesg {
OSIoMesgHdr hdr; /* Message header */
PTR(void) dramAddr; /* RDRAM buffer address (DMA) */
u32 devAddr; /* Device buffer address (DMA) */
u32 size; /* DMA transfer size in bytes */
u32 piHandle; /* PI device handle */
OSIoMesgHdr hdr; /* Message header */
PTR(void) dramAddr; /* RDRAM buffer address (DMA) */
u32 devAddr; /* Device buffer address (DMA) */
u32 size; /* DMA transfer size in bytes */
u32 piHandle; /* PI device handle */
};
struct OSPiHandle {
PTR(OSPiHandle_s) unused; /* point to next handle on the table */
PTR(OSPiHandle_s) unused; /* point to next handle on the table */
// These four members reversed due to endianness
u8 relDuration; /* domain release duration */
u8 pageSize; /* domain page size */
u8 latency; /* domain latency */
u8 type; /* DEVICE_TYPE_BULK for disk */
u8 relDuration; /* domain release duration */
u8 pageSize; /* domain page size */
u8 latency; /* domain latency */
u8 type; /* DEVICE_TYPE_BULK for disk */
// These three members reversed due to endianness
u16 padding; /* struct alignment padding */
u8 domain; /* which domain */
u8 pulse; /* domain pulse width */
u32 baseAddress; /* Domain address */
u32 speed; /* for roms only */
u16 padding; /* struct alignment padding */
u8 domain; /* which domain */
u8 pulse; /* domain pulse width */
u32 baseAddress; /* Domain address */
u32 speed; /* for roms only */
/* The following are "private" elements" */
u32 transferInfo[18]; /* for disk only */
u32 transferInfo[18]; /* for disk only */
};
typedef struct {
u32 ctrl;
u32 width;
u32 burst;
u32 vSync;
u32 hSync;
u32 leap;
u32 hStart;
u32 xScale;
u32 vCurrent;
u32 ctrl;
u32 width;
u32 burst;
u32 vSync;
u32 hSync;
u32 leap;
u32 hStart;
u32 xScale;
u32 vCurrent;
} OSViCommonRegs;
typedef struct {
u32 origin;
u32 yScale;
u32 vStart;
u32 vBurst;
u32 vIntr;
u32 origin;
u32 yScale;
u32 vStart;
u32 vBurst;
u32 vIntr;
} OSViFieldRegs;
typedef struct {
u8 padding[3];
u8 type;
OSViCommonRegs comRegs;
OSViFieldRegs fldRegs[2];
OSViFieldRegs fldRegs[2];
} OSViMode;
/*
@ -221,13 +221,13 @@ typedef struct {
int status;
PTR(OSMesgQueue) queue;
int channel;
u8 id[32]; // TODO: funky endianness here
u8 id[32]; // TODO: funky endianness here
u8 label[32]; // TODO: funky endianness here
int version;
int dir_size;
int inode_table; /* block location */
int minode_table; /* mirrioring inode_table */
int dir_table; /* block location */
int inode_table; /* block location */
int minode_table; /* mirrioring inode_table */
int dir_table; /* block location */
int inode_start_page; /* page # */
// Padding and reversed members due to endianness
u8 padding[2];
@ -235,14 +235,13 @@ typedef struct {
u8 banks;
} OSPfs;
// Controller
typedef struct {
// These three members reversed due to endianness
u8 err_no;
u8 status; /* Controller status */
u16 type; /* Controller Type */
u8 status; /* Controller status */
u16 type; /* Controller Type */
} OSContStatus;
typedef struct {
@ -252,7 +251,6 @@ typedef struct {
u8 err_no;
} OSContPad;
///////////////
// Functions //
///////////////
@ -263,7 +261,7 @@ extern "C" {
void osInitialize(void);
typedef void (thread_func_t)(PTR(void));
typedef void(thread_func_t)(PTR(void));
void osCreateThread(RDRAM_ARG PTR(OSThread) t, OSId id, PTR(thread_func_t) entry, PTR(void) arg, PTR(void) sp, OSPri p);
void osStartThread(RDRAM_ARG PTR(OSThread) t);

View file

@ -1,10 +1,10 @@
#ifndef __ultramodern_HPP__
#define __ultramodern_HPP__
#include <thread>
#include <cassert>
#include <stdexcept>
#include <span>
#include <stdexcept>
#include <thread>
#undef MOODYCAMEL_DELETE_FUNCTION
#define MOODYCAMEL_DELETE_FUNCTION = delete
@ -14,8 +14,8 @@
#include "ultramodern/error_handling.hpp"
#include "ultramodern/events.hpp"
#include "ultramodern/input.hpp"
#include "ultramodern/rsp.hpp"
#include "ultramodern/renderer_context.hpp"
#include "ultramodern/rsp.hpp"
struct UltraThreadContext {
std::thread host_thread;
@ -25,127 +25,122 @@ struct UltraThreadContext {
namespace ultramodern {
// We need a place in rdram to hold the PI handles, so pick an address in extended rdram
constexpr uint32_t rdram_size = 1024 * 1024 * 16; // 16MB to give extra room for anything custom
constexpr int32_t cart_handle = 0x80800000;
constexpr int32_t drive_handle = (int32_t)(cart_handle + sizeof(OSPiHandle));
constexpr int32_t flash_handle = (int32_t)(drive_handle + sizeof(OSPiHandle));
constexpr uint32_t save_size = 1024 * 1024 / 8; // Maximum save size, 1Mbit for flash
// We need a place in rdram to hold the PI handles, so pick an address in extended rdram
constexpr uint32_t rdram_size = 1024 * 1024 * 16; // 16MB to give extra room for anything custom
constexpr int32_t cart_handle = 0x80800000;
constexpr int32_t drive_handle = (int32_t)(cart_handle + sizeof(OSPiHandle));
constexpr int32_t flash_handle = (int32_t)(drive_handle + sizeof(OSPiHandle));
constexpr uint32_t save_size = 1024 * 1024 / 8; // Maximum save size, 1Mbit for flash
// Initialization.
void preinit(RDRAM_ARG renderer::WindowHandle window_handle);
void init_saving(RDRAM_ARG1);
void init_events(RDRAM_ARG renderer::WindowHandle window_handle);
void init_timers(RDRAM_ARG1);
void init_thread_cleanup();
// Initialization.
void preinit(RDRAM_ARG renderer::WindowHandle window_handle);
void init_saving(RDRAM_ARG1);
void init_events(RDRAM_ARG renderer::WindowHandle window_handle);
void init_timers(RDRAM_ARG1);
void init_thread_cleanup();
// Thread queues.
constexpr PTR(PTR(OSThread)) running_queue = (PTR(PTR(OSThread)))-1;
// Thread queues.
constexpr PTR(PTR(OSThread)) running_queue = (PTR(PTR(OSThread)))-1;
void thread_queue_insert(RDRAM_ARG PTR(PTR(OSThread)) queue, PTR(OSThread) toadd);
PTR(OSThread) thread_queue_pop(RDRAM_ARG PTR(PTR(OSThread)) queue);
bool thread_queue_remove(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(OSThread) t_);
bool thread_queue_empty(RDRAM_ARG PTR(PTR(OSThread)) queue);
PTR(OSThread) thread_queue_peek(RDRAM_ARG PTR(PTR(OSThread)) queue);
void thread_queue_insert(RDRAM_ARG PTR(PTR(OSThread)) queue, PTR(OSThread) toadd);
PTR(OSThread) thread_queue_pop(RDRAM_ARG PTR(PTR(OSThread)) queue);
bool thread_queue_remove(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(OSThread) t_);
bool thread_queue_empty(RDRAM_ARG PTR(PTR(OSThread)) queue);
PTR(OSThread) thread_queue_peek(RDRAM_ARG PTR(PTR(OSThread)) queue);
// Message queues.
void wait_for_external_message(RDRAM_ARG1);
void wait_for_external_message_timed(RDRAM_ARG1, u32 millis);
// Message queues.
void wait_for_external_message(RDRAM_ARG1);
void wait_for_external_message_timed(RDRAM_ARG1, u32 millis);
// Thread scheduling.
void check_running_queue(RDRAM_ARG1);
void run_next_thread_and_wait(RDRAM_ARG1);
void resume_thread_and_wait(RDRAM_ARG OSThread* t);
void schedule_running_thread(RDRAM_ARG PTR(OSThread) t);
void cleanup_thread(UltraThreadContext* thread_context);
uint32_t permanent_thread_count();
uint32_t temporary_thread_count();
struct thread_terminated : std::exception {};
// Thread scheduling.
void check_running_queue(RDRAM_ARG1);
void run_next_thread_and_wait(RDRAM_ARG1);
void resume_thread_and_wait(RDRAM_ARG OSThread *t);
void schedule_running_thread(RDRAM_ARG PTR(OSThread) t);
void cleanup_thread(UltraThreadContext *thread_context);
uint32_t permanent_thread_count();
uint32_t temporary_thread_count();
struct thread_terminated : std::exception {};
enum class ThreadPriority {
Low,
Normal,
High,
VeryHigh,
Critical
};
enum class ThreadPriority {
Low,
Normal,
High,
VeryHigh,
Critical
};
void set_native_thread_name(const std::string& name);
void set_native_thread_priority(ThreadPriority pri);
PTR(OSThread) this_thread();
void set_main_thread();
bool is_game_thread();
void submit_rsp_task(RDRAM_ARG PTR(OSTask) task);
void send_si_message(RDRAM_ARG1);
uint32_t get_speed_multiplier();
void set_native_thread_name(const std::string& name);
void set_native_thread_priority(ThreadPriority pri);
PTR(OSThread) this_thread();
void set_main_thread();
bool is_game_thread();
void submit_rsp_task(RDRAM_ARG PTR(OSTask) task);
void send_si_message(RDRAM_ARG1);
uint32_t get_speed_multiplier();
// Time
std::chrono::high_resolution_clock::time_point get_start();
std::chrono::high_resolution_clock::duration time_since_start();
void measure_input_latency();
void sleep_milliseconds(uint32_t millis);
void sleep_until(const std::chrono::high_resolution_clock::time_point& time_point);
// Time
std::chrono::high_resolution_clock::time_point get_start();
std::chrono::high_resolution_clock::duration time_since_start();
void measure_input_latency();
void sleep_milliseconds(uint32_t millis);
void sleep_until(const std::chrono::high_resolution_clock::time_point& time_point);
// Graphics
uint32_t get_target_framerate(uint32_t original);
uint32_t get_display_refresh_rate();
float get_resolution_scale();
void load_shader_cache(std::span<const char> cache_data);
void trigger_config_action();
// Graphics
uint32_t get_target_framerate(uint32_t original);
uint32_t get_display_refresh_rate();
float get_resolution_scale();
void load_shader_cache(std::span<const char> cache_data);
void trigger_config_action();
// Audio
void init_audio();
void set_audio_frequency(uint32_t freq);
void queue_audio_buffer(RDRAM_ARG PTR(s16) audio_data, uint32_t byte_count);
uint32_t get_remaining_audio_bytes();
// Audio
void init_audio();
void set_audio_frequency(uint32_t freq);
void queue_audio_buffer(RDRAM_ARG PTR(s16) audio_data, uint32_t byte_count);
uint32_t get_remaining_audio_bytes();
struct audio_callbacks_t {
using queue_samples_t = void(int16_t*, size_t);
using get_samples_remaining_t = size_t();
using set_frequency_t = void(uint32_t);
queue_samples_t* queue_samples;
get_samples_remaining_t* get_frames_remaining;
set_frequency_t* set_frequency;
};
struct audio_callbacks_t {
using queue_samples_t = void(int16_t *, size_t);
using get_samples_remaining_t = size_t();
using set_frequency_t = void(uint32_t);
queue_samples_t *queue_samples;
get_samples_remaining_t *get_frames_remaining;
set_frequency_t *set_frequency;
};
// TODO: Most of the members of this struct are not used by ultramodern. Should we move them to librecomp instead?
struct gfx_callbacks_t {
using gfx_data_t = void*;
using create_gfx_t = gfx_data_t();
using create_window_t = renderer::WindowHandle(gfx_data_t);
using update_gfx_t = void(gfx_data_t);
// TODO: Most of the members of this struct are not used by ultramodern. Should we move them to librecomp instead?
struct gfx_callbacks_t {
using gfx_data_t = void *;
using create_gfx_t = gfx_data_t();
using create_window_t = renderer::WindowHandle(gfx_data_t);
using update_gfx_t = void(gfx_data_t);
create_gfx_t* create_gfx;
create_window_t* create_window;
update_gfx_t* update_gfx;
};
create_gfx_t *create_gfx;
create_window_t *create_window;
update_gfx_t *update_gfx;
};
bool is_game_started();
void quit();
void join_event_threads();
void join_thread_cleaner_thread();
void join_saving_thread();
bool is_game_started();
void quit();
void join_event_threads();
void join_thread_cleaner_thread();
void join_saving_thread();
void set_audio_callbacks(const audio_callbacks_t& callbacks);
void set_audio_callbacks(const audio_callbacks_t& callbacks);
/**
* Register all the callbacks used by `ultramodern`, most of them being optional.
*
* The following arguments contain mandatory callbacks that need to be registered (i.e., can't be `nullptr`):
* - `rsp_callbacks`
* - `renderer_callbacks`
*
* It must be called only once and it must be called before `ultramodern::preinit`.
*/
void set_callbacks(
const rsp::callbacks_t& rsp_callbacks,
const renderer::callbacks_t& renderer_callbacks,
const audio_callbacks_t& audio_callbacks,
const input::callbacks_t& input_callbacks,
const gfx_callbacks_t& gfx_callbacks,
const events::callbacks_t& events_callbacks,
const error_handling::callbacks_t& error_handling_callbacks
);
/**
* Register all the callbacks used by `ultramodern`, most of them being optional.
*
* The following arguments contain mandatory callbacks that need to be registered (i.e., can't be `nullptr`):
* - `rsp_callbacks`
* - `renderer_callbacks`
*
* It must be called only once and it must be called before `ultramodern::preinit`.
*/
void set_callbacks(
const rsp::callbacks_t& rsp_callbacks, const renderer::callbacks_t& renderer_callbacks, const audio_callbacks_t& audio_callbacks,
const input::callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks, const events::callbacks_t& events_callbacks,
const error_handling::callbacks_t& error_handling_callbacks);
} // namespace ultramodern
#define MIN(a, b) ((a) < (b) ? (a) : (b))

View file

@ -36,7 +36,7 @@ void ultramodern::queue_audio_buffer(RDRAM_ARG PTR(int16_t) audio_data_, uint32_
}
// For SDL2
//uint32_t buffer_offset_frames = 1;
// uint32_t buffer_offset_frames = 1;
// For Godot
float buffer_offset_frames = 0.5f;
@ -52,16 +52,16 @@ uint32_t ultramodern::get_remaining_audio_bytes() {
else {
buffered_byte_count = 100;
}
// Adjust the reported count to be some number of refreshes in the future, which helps ensure that
// there are enough samples even if the audio thread experiences a small amount of lag. This prevents
// audio popping on games that use the buffered audio byte count to determine how many samples
// to generate.
uint32_t samples_per_vi = (sample_rate / 60);
if (buffered_byte_count > static_cast<uint32_t>(buffer_offset_frames * sizeof(int16_t) * samples_per_vi)) {
buffered_byte_count -= static_cast<uint32_t>(buffer_offset_frames * sizeof(int16_t) * samples_per_vi);
}
else {
buffered_byte_count = 0;
}
return buffered_byte_count;
// Adjust the reported count to be some number of refreshes in the future, which helps ensure that
// there are enough samples even if the audio thread experiences a small amount of lag. This prevents
// audio popping on games that use the buffered audio byte count to determine how many samples
// to generate.
uint32_t samples_per_vi = (sample_rate / 60);
if (buffered_byte_count > static_cast<uint32_t>(buffer_offset_frames * sizeof(int16_t) * samples_per_vi)) {
buffered_byte_count -= static_cast<uint32_t>(buffer_offset_frames * sizeof(int16_t) * samples_per_vi);
}
else {
buffered_byte_count = 0;
}
return buffered_byte_count;
}

View file

@ -8,7 +8,7 @@ void ultramodern::error_handling::set_callbacks(const ultramodern::error_handlin
error_handling_callbacks = callbacks;
}
void ultramodern::error_handling::message_box(const char* msg) {
void ultramodern::error_handling::message_box(const char *msg) {
// We print the message to stderr since the user may not have provided a message_box callback
fprintf(stderr, "%s\n", msg);
@ -18,7 +18,7 @@ void ultramodern::error_handling::message_box(const char* msg) {
}
}
void ultramodern::error_handling::quick_exit(const char* filename, int line, const char *func, int exit_status) {
void ultramodern::error_handling::quick_exit(const char *filename, int line, const char *func, int exit_status) {
fprintf(stderr, "Exiting with exit status '%i'. Function %s, at file %s:%i, ", exit_status, func, filename, line);
#ifdef __APPLE__

View file

@ -1,21 +1,21 @@
#include <thread>
#include <atomic>
#include <chrono>
#include <cinttypes>
#include <variant>
#include <unordered_map>
#include <utility>
#include <cstring>
#include <mutex>
#include <queue>
#include <cstring>
#include <thread>
#include <unordered_map>
#include <utility>
#include <variant>
#include "blockingconcurrentqueue.h"
#include "ultramodern/ultra64.h"
#include "ultramodern/ultramodern.hpp"
#include "ultramodern/rsp.hpp"
#include "ultramodern/renderer_context.hpp"
#include "ultramodern/rsp.hpp"
static ultramodern::events::callbacks_t events_callbacks{};
@ -31,8 +31,7 @@ struct SwapBuffersAction {
uint32_t origin;
};
struct UpdateConfigAction {
};
struct UpdateConfigAction {};
struct LoadShaderCacheAction {
std::span<const char> data;
@ -69,14 +68,14 @@ static struct {
} si;
// The same message queue may be used for multiple events, so share a mutex for all of them
std::mutex message_mutex;
uint8_t* rdram;
uint8_t *rdram;
moodycamel::BlockingConcurrentQueue<Action> action_queue{};
moodycamel::BlockingConcurrentQueue<OSTask*> sp_task_queue{};
moodycamel::ConcurrentQueue<OSThread*> deleted_threads{};
moodycamel::BlockingConcurrentQueue<OSTask *> sp_task_queue{};
moodycamel::ConcurrentQueue<OSThread *> deleted_threads{};
} events_context{};
extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, OSMesg msg) {
OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_);
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
std::lock_guard lock{ events_context.message_mutex };
switch (event_id) {
@ -107,7 +106,6 @@ extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 ret
uint64_t total_vis = 0;
extern std::atomic_bool exited;
void set_dummy_vi();
@ -124,15 +122,16 @@ void vi_thread_func() {
while (!exited) {
// Determine the next VI time (more accurate than adding 16ms each VI interrupt)
auto next = ultramodern::get_start() + (total_vis * 1000000us) / (60 * ultramodern::get_speed_multiplier());
//if (next > std::chrono::high_resolution_clock::now()) {
// printf("Sleeping for %" PRIu64 " us to get from %" PRIu64 " us to %" PRIu64 " us \n",
// (next - std::chrono::high_resolution_clock::now()) / 1us,
// (std::chrono::high_resolution_clock::now() - events_context.start) / 1us,
// (next - events_context.start) / 1us);
//} else {
// printf("No need to sleep\n");
//}
// Detect if there's more than a second to wait and wait a fixed amount instead for the next VI if so, as that usually means the system clock went back in time.
// if (next > std::chrono::high_resolution_clock::now()) {
// printf("Sleeping for %" PRIu64 " us to get from %" PRIu64 " us to %" PRIu64 " us \n",
// (next - std::chrono::high_resolution_clock::now()) / 1us,
// (std::chrono::high_resolution_clock::now() - events_context.start) / 1us,
// (next - events_context.start) / 1us);
// } else {
// printf("No need to sleep\n");
// }
// Detect if there's more than a second to wait and wait a fixed amount instead for the next VI if so, as that usually means the
// system clock went back in time.
if (std::chrono::floor<std::chrono::seconds>(next - std::chrono::high_resolution_clock::now()) > 1s) {
// printf("Skipping the next VI wait\n");
next = std::chrono::high_resolution_clock::now();
@ -141,7 +140,7 @@ void vi_thread_func() {
// Calculate how many VIs have passed
uint64_t new_total_vis = (ultramodern::time_since_start() * (60 * ultramodern::get_speed_multiplier()) / 1000ms) + 1;
if (new_total_vis > total_vis + 1) {
//printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1);
// printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1);
}
total_vis = new_total_vis;
@ -149,14 +148,14 @@ void vi_thread_func() {
{
std::lock_guard lock{ events_context.message_mutex };
uint8_t* rdram = events_context.rdram;
uint8_t *rdram = events_context.rdram;
if (remaining_retraces == 0) {
remaining_retraces = events_context.vi.retrace_count;
if (ultramodern::is_game_started()) {
if (events_context.vi.mq != NULLPTR) {
if (osSendMesg(PASS_RDRAM events_context.vi.mq, events_context.vi.msg, OS_MESG_NOBLOCK) == -1) {
//printf("Game skipped a VI frame!\n");
// printf("Game skipped a VI frame!\n");
}
}
}
@ -174,7 +173,7 @@ void vi_thread_func() {
}
if (events_context.ai.mq != NULLPTR) {
if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) {
//printf("Game skipped a AI frame!\n");
// printf("Game skipped a AI frame!\n");
}
}
}
@ -186,18 +185,18 @@ void vi_thread_func() {
}
void sp_complete() {
uint8_t* rdram = events_context.rdram;
uint8_t *rdram = events_context.rdram;
std::lock_guard lock{ events_context.message_mutex };
osSendMesg(PASS_RDRAM events_context.sp.mq, events_context.sp.msg, OS_MESG_NOBLOCK);
}
void dp_complete() {
uint8_t* rdram = events_context.rdram;
uint8_t *rdram = events_context.rdram;
std::lock_guard lock{ events_context.message_mutex };
osSendMesg(PASS_RDRAM events_context.dp.mq, events_context.dp.msg, OS_MESG_NOBLOCK);
}
void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready) {
void task_thread_func(uint8_t *rdram, moodycamel::LightweightSemaphore *thread_ready) {
ultramodern::set_native_thread_name("SP Task Thread");
ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal);
@ -206,7 +205,7 @@ void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_r
while (true) {
// Wait until an RSP task has been sent
OSTask* task;
OSTask *task;
events_context.sp_task_queue.wait_dequeue(task);
if (task == nullptr) {
@ -249,7 +248,7 @@ float ultramodern::get_resolution_scale() {
}
void ultramodern::load_shader_cache(std::span<const char> cache_data) {
events_context.action_queue.enqueue(LoadShaderCacheAction{cache_data});
events_context.action_queue.enqueue(LoadShaderCacheAction{ cache_data });
}
void ultramodern::trigger_config_action() {
@ -258,7 +257,7 @@ void ultramodern::trigger_config_action() {
std::atomic<ultramodern::renderer::SetupResult> renderer_setup_result = ultramodern::renderer::SetupResult::Success;
void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready, ultramodern::renderer::WindowHandle window_handle) {
void gfx_thread_func(uint8_t *rdram, moodycamel::LightweightSemaphore *thread_ready, ultramodern::renderer::WindowHandle window_handle) {
bool enabled_instant_present = false;
using namespace std::chrono_literals;
@ -267,7 +266,8 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
auto old_config = ultramodern::renderer::get_graphics_config();
auto renderer_context = ultramodern::renderer::create_render_context(rdram, window_handle, ultramodern::renderer::get_graphics_config().developer_mode);
auto renderer_context =
ultramodern::renderer::create_render_context(rdram, window_handle, ultramodern::renderer::get_graphics_config().developer_mode);
if (!renderer_context->valid()) {
renderer_setup_result.store(renderer_context->get_setup_result());
@ -290,7 +290,7 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
Action action;
if (events_context.action_queue.wait_dequeue_timed(action, 1ms)) {
// Determine the action type and act on it
if (const auto* task_action = std::get_if<SpTaskAction>(&action)) {
if (const auto *task_action = std::get_if<SpTaskAction>(&action)) {
// Turn on instant present if the game has been started and it hasn't been turned on yet.
if (ultramodern::is_game_started() && !enabled_instant_present) {
renderer_context->enable_instant_present();
@ -307,21 +307,22 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
renderer_context->send_dl(&task_action->task);
auto renderer_end = std::chrono::high_resolution_clock::now();
dp_complete();
// printf("Renderer ProcessDList time: %d us\n", static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(renderer_end - renderer_start).count()));
// printf("Renderer ProcessDList time: %d us\n",
// static_cast<u32>(std::chrono::duration_cast<std::chrono::microseconds>(renderer_end - renderer_start).count()));
}
else if (const auto* swap_action = std::get_if<SwapBuffersAction>(&action)) {
else if (const auto *swap_action = std::get_if<SwapBuffersAction>(&action)) {
events_context.vi.current_buffer = events_context.vi.next_buffer;
renderer_context->update_screen(swap_action->origin);
display_refresh_rate = renderer_context->get_display_framerate();
resolution_scale = renderer_context->get_resolution_scale();
}
else if (const auto* config_action = std::get_if<UpdateConfigAction>(&action)) {
else if (const auto *config_action = std::get_if<UpdateConfigAction>(&action)) {
auto new_config = ultramodern::renderer::get_graphics_config();
if (renderer_context->update_config(old_config, new_config)) {
old_config = new_config;
}
}
else if (const auto* load_shader_cache_action = std::get_if<LoadShaderCacheAction>(&action)) {
else if (const auto *load_shader_cache_action = std::get_if<LoadShaderCacheAction>(&action)) {
renderer_context->load_shader_cache(load_shader_cache_action->data);
}
}
@ -368,7 +369,8 @@ void set_dummy_vi() {
extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) {
if (vi_black) {
VI_H_START_REG = 0;
} else {
}
else {
VI_H_START_REG = hstart;
}
events_context.vi.next_buffer = frameBufPtr;
@ -376,7 +378,7 @@ extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) {
}
extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) {
OSViMode* mode = TO_PTR(OSViMode, mode_);
OSViMode *mode = TO_PTR(OSViMode, mode_);
VI_STATUS_REG = mode->comRegs.ctrl;
VI_WIDTH_REG = mode->comRegs.width;
// burst
@ -395,29 +397,29 @@ extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) {
VI_INTR_REG = mode->fldRegs[0].vIntr;
}
#define VI_CTRL_TYPE_16 0x00002
#define VI_CTRL_TYPE_32 0x00003
#define VI_CTRL_GAMMA_DITHER_ON 0x00004
#define VI_CTRL_GAMMA_ON 0x00008
#define VI_CTRL_DIVOT_ON 0x00010
#define VI_CTRL_SERRATE_ON 0x00040
#define VI_CTRL_ANTIALIAS_MASK 0x00300
#define VI_CTRL_ANTIALIAS_MODE_1 0x00100
#define VI_CTRL_ANTIALIAS_MODE_2 0x00200
#define VI_CTRL_ANTIALIAS_MODE_3 0x00300
#define VI_CTRL_PIXEL_ADV_MASK 0x01000
#define VI_CTRL_PIXEL_ADV_1 0x01000
#define VI_CTRL_PIXEL_ADV_2 0x02000
#define VI_CTRL_PIXEL_ADV_3 0x03000
#define VI_CTRL_DITHER_FILTER_ON 0x10000
#define VI_CTRL_TYPE_16 0x00002
#define VI_CTRL_TYPE_32 0x00003
#define VI_CTRL_GAMMA_DITHER_ON 0x00004
#define VI_CTRL_GAMMA_ON 0x00008
#define VI_CTRL_DIVOT_ON 0x00010
#define VI_CTRL_SERRATE_ON 0x00040
#define VI_CTRL_ANTIALIAS_MASK 0x00300
#define VI_CTRL_ANTIALIAS_MODE_1 0x00100
#define VI_CTRL_ANTIALIAS_MODE_2 0x00200
#define VI_CTRL_ANTIALIAS_MODE_3 0x00300
#define VI_CTRL_PIXEL_ADV_MASK 0x01000
#define VI_CTRL_PIXEL_ADV_1 0x01000
#define VI_CTRL_PIXEL_ADV_2 0x02000
#define VI_CTRL_PIXEL_ADV_3 0x03000
#define VI_CTRL_DITHER_FILTER_ON 0x10000
#define OS_VI_GAMMA_ON 0x0001
#define OS_VI_GAMMA_OFF 0x0002
#define OS_VI_GAMMA_DITHER_ON 0x0004
#define OS_VI_GAMMA_DITHER_OFF 0x0008
#define OS_VI_DIVOT_ON 0x0010
#define OS_VI_DIVOT_OFF 0x0020
#define OS_VI_DITHER_FILTER_ON 0x0040
#define OS_VI_GAMMA_ON 0x0001
#define OS_VI_GAMMA_OFF 0x0002
#define OS_VI_GAMMA_DITHER_ON 0x0004
#define OS_VI_GAMMA_DITHER_OFF 0x0008
#define OS_VI_DIVOT_ON 0x0010
#define OS_VI_DIVOT_OFF 0x0020
#define OS_VI_DITHER_FILTER_ON 0x0040
#define OS_VI_DITHER_FILTER_OFF 0x0080
extern "C" void osViSetSpecialFeatures(uint32_t func) {
@ -452,7 +454,7 @@ extern "C" void osViSetSpecialFeatures(uint32_t func) {
if ((func & OS_VI_DITHER_FILTER_OFF) != 0) {
VI_STATUS_REG &= ~VI_CTRL_DITHER_FILTER_ON;
//VI_STATUS_REG |= __osViNext->modep->comRegs.ctrl & VI_CTRL_ANTIALIAS_MASK;
// VI_STATUS_REG |= __osViNext->modep->comRegs.ctrl & VI_CTRL_ANTIALIAS_MASK;
}
}
@ -481,7 +483,7 @@ extern "C" PTR(void) osViGetCurrentFramebuffer() {
}
void ultramodern::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) {
OSTask* task = TO_PTR(OSTask, task_);
OSTask *task = TO_PTR(OSTask, task_);
// Send gfx tasks to the graphics action queue
if (task->t.type == M_GFXTASK) {
@ -525,10 +527,14 @@ void ultramodern::init_events(RDRAM_ARG ultramodern::renderer::WindowHandle wind
show_renderer_error("Failed to load dynamic libraries. Make sure the DLLs are next to the recomp executable.");
break;
case ultramodern::renderer::SetupResult::InvalidGraphicsAPI:
show_renderer_error(ultramodern::renderer::get_graphics_api_name(ultramodern::renderer::get_graphics_config()) + " is not supported on this platform. Please select a different graphics API.");
show_renderer_error(
ultramodern::renderer::get_graphics_api_name(ultramodern::renderer::get_graphics_config()) +
" is not supported on this platform. Please select a different graphics API.");
break;
case ultramodern::renderer::SetupResult::GraphicsAPINotFound:
show_renderer_error("Unable to initialize " + ultramodern::renderer::get_graphics_api_name(ultramodern::renderer::get_graphics_config()) + "." + driver_os_suffix);
show_renderer_error(
"Unable to initialize " + ultramodern::renderer::get_graphics_api_name(ultramodern::renderer::get_graphics_config()) +
"." + driver_os_suffix);
break;
case ultramodern::renderer::SetupResult::GraphicsDeviceNotFound:
show_renderer_error("Unable to find compatible graphics device." + driver_os_suffix);

View file

@ -4,7 +4,7 @@
#include "ultramodern/ultra64.h"
#include "ultramodern/ultramodern.hpp"
static ultramodern::input::callbacks_t input_callbacks {};
static ultramodern::input::callbacks_t input_callbacks{};
void ultramodern::input::set_callbacks(const callbacks_t& callbacks) {
input_callbacks = callbacks;
@ -27,8 +27,8 @@ void ultramodern::measure_input_latency() {
#define CONT_NO_RESPONSE_ERROR 0x8
#define CONT_TYPE_NORMAL 0x0005
#define CONT_TYPE_MOUSE 0x0002
#define CONT_TYPE_VOICE 0x0100
#define CONT_TYPE_MOUSE 0x0002
#define CONT_TYPE_VOICE 0x0100
static int max_controllers = 0;
@ -36,11 +36,11 @@ static int max_controllers = 0;
static u16 get_controller_type(ultramodern::input::Device device_type) {
switch (device_type) {
case ultramodern::input::Device::None:
return 0;
case ultramodern::input::Device::None:
return 0;
case ultramodern::input::Device::Controller:
return CONT_TYPE_NORMAL;
case ultramodern::input::Device::Controller:
return CONT_TYPE_NORMAL;
#if 0
case ultramodern::input::Device::Mouse:
@ -54,7 +54,7 @@ static u16 get_controller_type(ultramodern::input::Device device_type) {
return 0;
}
static void __osContGetInitData(u8* pattern, OSContStatus *data) {
static void __osContGetInitData(u8 *pattern, OSContStatus *data) {
*pattern = 0x00;
for (int controller = 0; controller < max_controllers; controller++) {
@ -144,8 +144,9 @@ extern "C" void osContGetReadData(OSContPad *data) {
data[controller].stick_x = (int8_t)(127 * x);
data[controller].stick_y = (int8_t)(127 * y);
data[controller].err_no = 0;
} else {
data[controller].err_no = CONT_NO_RESPONSE_ERROR; // CHNL_ERR_NORESP >> 4
}
else {
data[controller].err_no = CONT_NO_RESPONSE_ERROR; // CHNL_ERR_NORESP >> 4
}
}
}

View file

@ -11,10 +11,10 @@ struct QueuedMessage {
bool jam;
};
static moodycamel::BlockingConcurrentQueue<QueuedMessage> external_messages {};
static moodycamel::BlockingConcurrentQueue<QueuedMessage> external_messages{};
void enqueue_external_message(PTR(OSMesgQueue) mq, OSMesg msg, bool jam) {
external_messages.enqueue({mq, msg, jam});
external_messages.enqueue({ mq, msg, jam });
}
bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block);
@ -34,7 +34,7 @@ void ultramodern::wait_for_external_message(RDRAM_ARG1) {
void ultramodern::wait_for_external_message_timed(RDRAM_ARG1, u32 millis) {
QueuedMessage to_send;
if (external_messages.wait_dequeue_timed(to_send, std::chrono::milliseconds{millis})) {
if (external_messages.wait_dequeue_timed(to_send, std::chrono::milliseconds{ millis })) {
do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false);
}
}
@ -57,12 +57,12 @@ s32 MQ_IS_EMPTY(OSMesgQueue *mq) {
return mq->validCount == 0;
}
s32 MQ_IS_FULL(OSMesgQueue* mq) {
s32 MQ_IS_FULL(OSMesgQueue *mq) {
return MQ_GET_COUNT(mq) >= mq->msgCount;
}
bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block) {
OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_);
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
if (!block) {
// If non-blocking, fail if the queue is full.
if (MQ_IS_FULL(mq)) {
@ -77,7 +77,7 @@ bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block) {
ultramodern::run_next_thread_and_wait(PASS_RDRAM1);
}
}
if (jam) {
// Jams insert at the head of the message queue's buffer.
mq->first = (mq->first + mq->msgCount - 1) % mq->msgCount;
@ -96,18 +96,19 @@ bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block) {
if (!ultramodern::thread_queue_empty(PASS_RDRAM blocked_queue)) {
ultramodern::schedule_running_thread(PASS_RDRAM ultramodern::thread_queue_pop(PASS_RDRAM blocked_queue));
}
return true;
}
bool do_recv(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, bool block) {
OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_);
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
if (!block) {
// If non-blocking, fail if the queue is empty
if (MQ_IS_EMPTY(mq)) {
return false;
}
} else {
}
else {
// Otherwise, yield this thread in a loop until the queue is no longer full
while (MQ_IS_EMPTY(mq)) {
debug_printf("[Message Queue] Thread %d is blocked on receive\n", TO_PTR(OSThread, ultramodern::this_thread())->id);
@ -119,7 +120,7 @@ bool do_recv(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, bool block) {
if (msg_ != NULLPTR) {
*TO_PTR(OSMesg, msg_) = TO_PTR(OSMesg, mq->msg)[mq->first];
}
mq->first = (mq->first + 1) % mq->msgCount;
mq->validCount--;
@ -135,19 +136,19 @@ bool do_recv(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, bool block) {
extern "C" s32 osSendMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) {
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
bool jam = false;
// Don't directly send to the message queue if this isn't a game thread to avoid contention.
if (!ultramodern::is_game_thread()) {
enqueue_external_message(mq_, msg, jam);
return 0;
}
// Handle any messages that have been received from an external thread.
dequeue_external_messages(PASS_RDRAM1);
// Try to send the message.
bool sent = do_send(PASS_RDRAM mq_, msg, jam, flags == OS_MESG_BLOCK);
// Check the queue to see if this thread should swap execution to another.
ultramodern::check_running_queue(PASS_RDRAM1);
@ -157,19 +158,19 @@ extern "C" s32 osSendMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags)
extern "C" s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) {
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
bool jam = true;
// Don't directly send to the message queue if this isn't a game thread to avoid contention.
if (!ultramodern::is_game_thread()) {
enqueue_external_message(mq_, msg, jam);
return 0;
}
// Handle any messages that have been received from an external thread.
dequeue_external_messages(PASS_RDRAM1);
// Try to send the message.
bool sent = do_send(PASS_RDRAM mq_, msg, jam, flags == OS_MESG_BLOCK);
// Check the queue to see if this thread should swap execution to another.
ultramodern::check_running_queue(PASS_RDRAM1);
@ -178,15 +179,15 @@ extern "C" s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags)
extern "C" s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, s32 flags) {
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
assert(ultramodern::is_game_thread() && "RecvMesg not allowed outside of game threads.");
// Handle any messages that have been received from an external thread.
dequeue_external_messages(PASS_RDRAM1);
// Try to receive a message.
bool received = do_recv(PASS_RDRAM mq_, msg_, flags == OS_MESG_BLOCK);
// Check the queue to see if this thread should swap execution to another.
ultramodern::check_running_queue(PASS_RDRAM1);

View file

@ -1,10 +1,10 @@
#include "ultramodern/ultra64.h"
#define K0BASE 0x80000000
#define K1BASE 0xA0000000
#define K2BASE 0xC0000000
#define IS_KSEG0(x) ((u32)(x) >= K0BASE && (u32)(x) < K1BASE)
#define IS_KSEG1(x) ((u32)(x) >= K1BASE && (u32)(x) < K2BASE)
#define K0BASE 0x80000000
#define K1BASE 0xA0000000
#define K2BASE 0xC0000000
#define IS_KSEG0(x) ((u32)(x) >= K0BASE && (u32)(x) < K1BASE)
#define IS_KSEG1(x) ((u32)(x) >= K1BASE && (u32)(x) < K2BASE)
#define K0_TO_PHYS(x) ((u32)(x)&0x1FFFFFFF)
#define K1_TO_PHYS(x) ((u32)(x)&0x1FFFFFFF)
@ -12,11 +12,12 @@ u32 osVirtualToPhysical(PTR(void) addr) {
uintptr_t addr_val = (uintptr_t)addr;
if (IS_KSEG0(addr_val)) {
return K0_TO_PHYS(addr_val);
} else if (IS_KSEG1(addr_val)) {
}
else if (IS_KSEG1(addr_val)) {
return K1_TO_PHYS(addr_val);
} else {
}
else {
// TODO handle TLB mappings
return (u32)addr_val;
}
}

View file

@ -1,10 +1,10 @@
#if 0
#include <stdio.h>
#include <stdlib.h>
#include "ultra64.h"
# include "ultra64.h"
# include <stdio.h>
# include <stdlib.h>
#define THREAD_STACK_SIZE 0x1000
# define THREAD_STACK_SIZE 0x1000
u8 idle_stack[THREAD_STACK_SIZE] ALIGNED(16);
u8 main_stack[THREAD_STACK_SIZE] ALIGNED(16);

View file

@ -10,8 +10,8 @@ void ultramodern::renderer::set_callbacks(const callbacks_t& callbacks) {
render_callbacks = callbacks;
}
std::unique_ptr<ultramodern::renderer::RendererContext> ultramodern::renderer::create_render_context(uint8_t* rdram, WindowHandle window_handle, bool developer_mode) {
std::unique_ptr<ultramodern::renderer::RendererContext>
ultramodern::renderer::create_render_context(uint8_t *rdram, WindowHandle window_handle, bool developer_mode) {
if (render_callbacks.create_render_context == nullptr) {
error_handling::message_box("[Error] The mandatory render callback `create_render_context` was not registered");
ULTRAMODERN_QUICK_EXIT();
@ -27,7 +27,6 @@ std::string ultramodern::renderer::get_graphics_api_name(const GraphicsConfig& c
return config.get_graphics_api_name();
}
static ultramodern::renderer::GraphicsConfig graphic_config{};
static std::mutex graphic_config_mutex;

View file

@ -3,7 +3,7 @@
#include "ultramodern/rsp.hpp"
static ultramodern::rsp::callbacks_t rsp_callbacks {};
static ultramodern::rsp::callbacks_t rsp_callbacks{};
void ultramodern::rsp::set_callbacks(const callbacks_t& callbacks) {
rsp_callbacks = callbacks;
@ -15,7 +15,7 @@ void ultramodern::rsp::init() {
}
}
bool ultramodern::rsp::run_task(RDRAM_ARG const OSTask* task) {
bool ultramodern::rsp::run_task(RDRAM_ARG const OSTask *task) {
assert(rsp_callbacks.run_task != nullptr);
return rsp_callbacks.run_task(PASS_RDRAM task);

View file

@ -19,8 +19,8 @@ void ultramodern::check_running_queue(RDRAM_ARG1) {
// Check if there are any threads in the running queue.
if (!thread_queue_empty(PASS_RDRAM running_queue)) {
// Check if the highest priority thread in the queue is higher priority than the current thread.
OSThread* next_thread = TO_PTR(OSThread, ultramodern::thread_queue_peek(PASS_RDRAM running_queue));
OSThread* self = TO_PTR(OSThread, ultramodern::this_thread());
OSThread *next_thread = TO_PTR(OSThread, ultramodern::thread_queue_peek(PASS_RDRAM running_queue));
OSThread *self = TO_PTR(OSThread, ultramodern::this_thread());
if (next_thread->priority > self->priority) {
ultramodern::thread_queue_pop(PASS_RDRAM running_queue);
// Swap to the higher priority thread.

View file

@ -1,9 +1,9 @@
#ifdef _WIN32
#include <Windows.h>
# include <Windows.h>
#include "ultramodern/ultra64.h"
#include "ultramodern/ultramodern.hpp"
# include "ultramodern/ultra64.h"
# include "ultramodern/ultramodern.hpp"
extern "C" unsigned int sleep(unsigned int seconds) {
Sleep(seconds * 1000);

View file

@ -4,7 +4,7 @@
static PTR(OSThread) running_queue_impl = NULLPTR;
static PTR(OSThread)* queue_to_ptr(RDRAM_ARG PTR(PTR(OSThread)) queue) {
static PTR(OSThread) *queue_to_ptr(RDRAM_ARG PTR(PTR(OSThread)) queue) {
if (queue == ultramodern::running_queue) {
return &running_queue_impl;
}
@ -12,8 +12,8 @@ static PTR(OSThread)* queue_to_ptr(RDRAM_ARG PTR(PTR(OSThread)) queue) {
}
void ultramodern::thread_queue_insert(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(OSThread) toadd_) {
PTR(OSThread)* cur = queue_to_ptr(PASS_RDRAM queue_);
OSThread* toadd = TO_PTR(OSThread, toadd_);
PTR(OSThread) *cur = queue_to_ptr(PASS_RDRAM queue_);
OSThread *toadd = TO_PTR(OSThread, toadd_);
debug_printf("[Thread Queue] Inserting thread %d into queue 0x%08X\n", toadd->id, (uintptr_t)queue_);
while (*cur && TO_PTR(OSThread, *cur)->priority > toadd->priority) {
cur = &TO_PTR(OSThread, *cur)->next;
@ -32,7 +32,7 @@ void ultramodern::thread_queue_insert(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(O
}
PTR(OSThread) ultramodern::thread_queue_pop(RDRAM_ARG PTR(PTR(OSThread)) queue_) {
PTR(OSThread)* queue = queue_to_ptr(PASS_RDRAM queue_);
PTR(OSThread) *queue = queue_to_ptr(PASS_RDRAM queue_);
PTR(OSThread) ret = *queue;
*queue = TO_PTR(OSThread, ret)->next;
TO_PTR(OSThread, ret)->queue = NULLPTR;
@ -45,7 +45,7 @@ bool ultramodern::thread_queue_remove(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(O
PTR(PTR(OSThread)) cur = queue_;
while (cur != NULLPTR) {
PTR(OSThread)* cur_ptr = queue_to_ptr(PASS_RDRAM queue_);
PTR(OSThread) *cur_ptr = queue_to_ptr(PASS_RDRAM queue_);
if (*cur_ptr == t_) {
return true;
}
@ -56,11 +56,11 @@ bool ultramodern::thread_queue_remove(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(O
}
bool ultramodern::thread_queue_empty(RDRAM_ARG PTR(PTR(OSThread)) queue_) {
PTR(OSThread)* queue = queue_to_ptr(PASS_RDRAM queue_);
PTR(OSThread) *queue = queue_to_ptr(PASS_RDRAM queue_);
return *queue == NULLPTR;
}
PTR(OSThread) ultramodern::thread_queue_peek(RDRAM_ARG PTR(PTR(OSThread)) queue_) {
PTR(OSThread)* queue = queue_to_ptr(PASS_RDRAM queue_);
PTR(OSThread) *queue = queue_to_ptr(PASS_RDRAM queue_);
return *queue;
}

View file

@ -1,15 +1,15 @@
#include <cstdio>
#include <thread>
#include <cassert>
#include <cstdio>
#include <string>
#include <thread>
#include "blockingconcurrentqueue.h"
#include "ultramodern/ultra64.h"
#include "ultramodern/ultramodern.hpp"
#include "blockingconcurrentqueue.h"
// Native APIs only used to set thread names for easier debugging
#ifdef _WIN32
#include <Windows.h>
# include <Windows.h>
#endif
extern "C" void bootproc();
@ -37,20 +37,17 @@ int main(int argc, char** argv) {
#endif
#if 1
void run_thread_function(uint8_t* rdram, uint64_t addr, uint64_t sp, uint64_t arg);
void run_thread_function(uint8_t *rdram, uint64_t addr, uint64_t sp, uint64_t arg);
#else
#define run_thread_function(func, sp, arg) func(arg)
# define run_thread_function(func, sp, arg) func(arg)
#endif
#if defined(_WIN32)
void ultramodern::set_native_thread_name(const std::string& name) {
std::wstring wname{name.begin(), name.end()};
std::wstring wname{ name.begin(), name.end() };
HRESULT r;
r = SetThreadDescription(
GetCurrentThread(),
wname.c_str()
);
r = SetThreadDescription(GetCurrentThread(), wname.c_str());
}
void ultramodern::set_native_thread_priority(ThreadPriority pri) {
@ -116,13 +113,14 @@ void ultramodern::set_native_thread_name(const std::string& name) {
pthread_setname_np(name.c_str());
}
void ultramodern::set_native_thread_priority(ThreadPriority pri) {}
void ultramodern::set_native_thread_priority(ThreadPriority pri) {
}
#endif
std::atomic_int temporary_threads = 0;
std::atomic_int permanent_threads = 0;
void wait_for_resumed(RDRAM_ARG UltraThreadContext* thread_context) {
void wait_for_resumed(RDRAM_ARG UltraThreadContext *thread_context) {
TO_PTR(OSThread, ultramodern::this_thread())->context->running.wait();
// If this thread's context was replaced by another thread or deleted, destroy it again from its own context.
// This will trigger thread cleanup instead.
@ -131,7 +129,7 @@ void wait_for_resumed(RDRAM_ARG UltraThreadContext* thread_context) {
}
}
void resume_thread(OSThread* t) {
void resume_thread(OSThread *t) {
debug_printf("[Thread] Resuming execution of thread %d\n", t->id);
t->context->running.signal();
}
@ -141,24 +139,24 @@ void run_next_thread(RDRAM_ARG1) {
throw std::runtime_error("No threads left to run!\n");
}
OSThread* to_run = TO_PTR(OSThread, ultramodern::thread_queue_pop(PASS_RDRAM ultramodern::running_queue));
OSThread *to_run = TO_PTR(OSThread, ultramodern::thread_queue_pop(PASS_RDRAM ultramodern::running_queue));
debug_printf("[Scheduling] Resuming execution of thread %d\n", to_run->id);
to_run->context->running.signal();
}
void ultramodern::run_next_thread_and_wait(RDRAM_ARG1) {
UltraThreadContext* cur_context = TO_PTR(OSThread, thread_self)->context;
UltraThreadContext *cur_context = TO_PTR(OSThread, thread_self)->context;
run_next_thread(PASS_RDRAM1);
wait_for_resumed(PASS_RDRAM cur_context);
}
void ultramodern::resume_thread_and_wait(RDRAM_ARG OSThread *t) {
UltraThreadContext* cur_context = TO_PTR(OSThread, thread_self)->context;
UltraThreadContext *cur_context = TO_PTR(OSThread, thread_self)->context;
resume_thread(t);
wait_for_resumed(PASS_RDRAM cur_context);
}
static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entrypoint, PTR(void) arg, UltraThreadContext* thread_context) {
static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entrypoint, PTR(void) arg, UltraThreadContext *thread_context) {
OSThread *self = TO_PTR(OSThread, self_);
debug_printf("[Thread] Thread created: %d\n", self->id);
thread_self = self_;
@ -183,7 +181,7 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry
// Wait until the thread is marked as running.
wait_for_resumed(PASS_RDRAM thread_context);
// Make sure the thread wasn't replaced or destroyed before it was started.
if (self->context == thread_context) {
debug_printf("[Thread] Thread started: %d\n", self->id);
@ -206,7 +204,7 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry
// Dispose of this thread now that it's completed or terminated.
ultramodern::cleanup_thread(thread_context);
// TODO fix these being hardcoded (this is only used for quicksaving)
if ((self->id == 2 && self->priority == 5) || self->id == 13) { // slowly, flashrom
temporary_threads.fetch_sub(1);
@ -222,7 +220,7 @@ uint32_t ultramodern::temporary_thread_count() {
}
extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) {
OSThread* t = TO_PTR(OSThread, t_);
OSThread *t = TO_PTR(OSThread, t_);
debug_printf("[os] Start Thread %d\n", t->id);
// Wait until the thread is initialized to indicate that it's ready to be started.
@ -239,14 +237,14 @@ extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) {
else {
t->state = OSThreadState::QUEUED;
resume_thread(t);
//throw ultramodern::thread_terminated{};
// throw ultramodern::thread_terminated{};
}
}
extern "C" void osCreateThread(RDRAM_ARG PTR(OSThread) t_, OSId id, PTR(thread_func_t) entrypoint, PTR(void) arg, PTR(void) sp, OSPri pri) {
debug_printf("[os] Create Thread %d\n", id);
OSThread *t = TO_PTR(OSThread, t_);
t->next = NULLPTR;
t->queue = NULLPTR;
t->priority = pri;
@ -257,7 +255,7 @@ extern "C" void osCreateThread(RDRAM_ARG PTR(OSThread) t_, OSId id, PTR(thread_f
// Spawn a new thread, which will immediately pause itself and wait until it's been started.
// Pass the context as an argument to the thread function to ensure that it can't get cleared before the thread captures its value.
t->context = new UltraThreadContext{};
t->context->host_thread = std::thread{_thread_func, PASS_RDRAM t_, entrypoint, arg, t->context};
t->context->host_thread = std::thread{ _thread_func, PASS_RDRAM t_, entrypoint, arg, t->context };
}
extern "C" void osStopThread(RDRAM_ARG PTR(OSThread) t_) {
@ -268,17 +266,17 @@ extern "C" void osDestroyThread(RDRAM_ARG PTR(OSThread) t_) {
if (t_ == NULLPTR) {
t_ = thread_self;
}
OSThread* t = TO_PTR(OSThread, t_);
OSThread *t = TO_PTR(OSThread, t_);
// Check if the thread is destroying itself (arg is null or thread_self)
if (t_ == thread_self) {
throw ultramodern::thread_terminated{};
}
// Otherwise if the thread isn't stopped, remove it from its currrent queue.,
// Otherwise if the thread isn't stopped, remove it from its currrent queue.,
if (t->state != OSThreadState::STOPPED) {
ultramodern::thread_queue_remove(PASS_RDRAM t->queue, t_);
}
// Check if the thread has already been destroyed to prevent destroying it again.
UltraThreadContext* cur_context = t->context;
UltraThreadContext *cur_context = t->context;
if (cur_context != nullptr) {
// Mark the target thread as destroyed and resume it. When it starts it'll check this and terminate itself instead of resuming.
t->context = nullptr;
@ -290,7 +288,7 @@ extern "C" void osSetThreadPri(RDRAM_ARG PTR(OSThread) t_, OSPri pri) {
if (t_ == NULLPTR) {
t_ = thread_self;
}
OSThread* t = TO_PTR(OSThread, t_);
OSThread *t = TO_PTR(OSThread, t_);
if (t->priority != pri) {
t->priority = pri;
@ -323,13 +321,13 @@ PTR(OSThread) ultramodern::this_thread() {
}
static std::thread thread_cleaner_thread;
static moodycamel::BlockingConcurrentQueue<UltraThreadContext*> deleted_threads{};
static moodycamel::BlockingConcurrentQueue<UltraThreadContext *> deleted_threads{};
extern std::atomic_bool exited;
void thread_cleaner_func() {
using namespace std::chrono_literals;
while (!exited) {
UltraThreadContext* to_delete;
UltraThreadContext *to_delete;
if (deleted_threads.wait_dequeue_timed(to_delete, 10ms)) {
debug_printf("[Cleanup] Deleting thread context %p\n", to_delete);
@ -340,7 +338,7 @@ void thread_cleaner_func() {
}
void ultramodern::init_thread_cleanup() {
thread_cleaner_thread = std::thread{thread_cleaner_func};
thread_cleaner_thread = std::thread{ thread_cleaner_func };
}
void ultramodern::cleanup_thread(UltraThreadContext *cur_context) {

View file

@ -1,14 +1,14 @@
#include "blockingconcurrentqueue.h"
#include <set>
#include <thread>
#include <variant>
#include <set>
#include "blockingconcurrentqueue.h"
#include "ultramodern/ultra64.h"
#include "ultramodern/ultramodern.hpp"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include "Windows.h"
# define WIN32_LEAN_AND_MEAN
# include "Windows.h"
#endif
// Start time for the program
@ -70,8 +70,8 @@ void timer_thread(RDRAM_ARG1) {
// Lambda comparator function to keep the set ordered
auto timer_sort = [PASS_RDRAM1](PTR(OSTimer) a_, PTR(OSTimer) b_) {
OSTimer* a = TO_PTR(OSTimer, a_);
OSTimer* b = TO_PTR(OSTimer, b_);
OSTimer *a = TO_PTR(OSTimer, a_);
OSTimer *b = TO_PTR(OSTimer, b_);
// Order by timestamp if the timers have different timestamps
if (a->timestamp != b->timestamp) {
@ -83,14 +83,15 @@ void timer_thread(RDRAM_ARG1) {
};
// Ordered set of timers that are currently active
std::set<PTR(OSTimer), decltype(timer_sort)> active_timers{timer_sort};
std::set<PTR(OSTimer), decltype(timer_sort)> active_timers{ timer_sort };
// Lambda to process a timer action to handle adding and removing timers
auto process_timer_action = [&](const Action& action) {
// Determine the action type and act on it
if (const auto* add_action = std::get_if<AddTimerAction>(&action)) {
if (const auto *add_action = std::get_if<AddTimerAction>(&action)) {
active_timers.insert(add_action->timer);
} else if (const auto* remove_action = std::get_if<RemoveTimerAction>(&action)) {
}
else if (const auto *remove_action = std::get_if<RemoveTimerAction>(&action)) {
active_timers.erase(remove_action->timer);
}
};
@ -110,7 +111,7 @@ void timer_thread(RDRAM_ARG1) {
// Get the timer that's closest to running out
PTR(OSTimer) cur_timer_ = *active_timers.begin();
OSTimer* cur_timer = TO_PTR(OSTimer, cur_timer_);
OSTimer *cur_timer = TO_PTR(OSTimer, cur_timer_);
// Remove the timer from the queue (it may get readded if waiting is interrupted)
active_timers.erase(cur_timer_);
@ -120,7 +121,7 @@ void timer_thread(RDRAM_ARG1) {
// Wait for either the duration to complete or a new action to come through
if (wait_duration.count() >= 0 && timer_context.action_queue.wait_dequeue_timed(cur_action, wait_duration)) {
// Timer was interrupted by a new action
// Timer was interrupted by a new action
// Add the current timer back to the queue (done first in case the action is to remove this timer)
active_timers.insert(cur_timer_);
// Process the new action
@ -169,13 +170,14 @@ extern "C" OSTime osGetTime() {
}
extern "C" int osSetTimer(RDRAM_ARG PTR(OSTimer) t_, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg) {
OSTimer* t = TO_PTR(OSTimer, t_);
OSTimer *t = TO_PTR(OSTimer, t_);
// Determine the time when this timer will trigger off
if (countdown == 0) {
// Set the timestamp based on the interval
t->timestamp = interval + time_now();
} else {
}
else {
t->timestamp = countdown + time_now();
}
t->interval = interval;
@ -214,7 +216,7 @@ void ultramodern::sleep_until(const std::chrono::high_resolution_clock::time_poi
#else
void ultramodern::sleep_milliseconds(uint32_t millis) {
std::this_thread::sleep_for(std::chrono::milliseconds{millis});
std::this_thread::sleep_for(std::chrono::milliseconds{ millis });
}
void ultramodern::sleep_until(const std::chrono::high_resolution_clock::time_point& time_point) {

View file

@ -2,14 +2,9 @@
#include "ultramodern/ultramodern.hpp"
void ultramodern::set_callbacks(
const rsp::callbacks_t& rsp_callbacks,
const renderer::callbacks_t& renderer_callbacks,
const audio_callbacks_t& audio_callbacks,
const input::callbacks_t& input_callbacks,
const gfx_callbacks_t& gfx_callbacks,
const events::callbacks_t& thread_callbacks,
const error_handling::callbacks_t& error_handling_callbacks
) {
const rsp::callbacks_t& rsp_callbacks, const renderer::callbacks_t& renderer_callbacks, const audio_callbacks_t& audio_callbacks,
const input::callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks, const events::callbacks_t& thread_callbacks,
const error_handling::callbacks_t& error_handling_callbacks) {
ultramodern::rsp::set_callbacks(rsp_callbacks);
ultramodern::renderer::set_callbacks(renderer_callbacks);
ultramodern::set_audio_callbacks(audio_callbacks);