2023-11-13 01:40:02 +01:00
|
|
|
#ifndef __ultramodern_HPP__
|
|
|
|
#define __ultramodern_HPP__
|
2023-02-20 04:27:35 +01:00
|
|
|
|
|
|
|
#include <thread>
|
2024-04-25 08:57:06 +02:00
|
|
|
#include <cassert>
|
|
|
|
#include <stdexcept>
|
2024-04-27 06:02:10 +02:00
|
|
|
#include <span>
|
2023-02-20 04:27:35 +01:00
|
|
|
|
2024-04-25 08:57:06 +02:00
|
|
|
#undef MOODYCAMEL_DELETE_FUNCTION
|
|
|
|
#define MOODYCAMEL_DELETE_FUNCTION = delete
|
|
|
|
#include "lightweightsemaphore.h"
|
2023-02-20 04:27:35 +01:00
|
|
|
#include "ultra64.h"
|
|
|
|
|
2023-10-23 21:32:30 +02:00
|
|
|
#if defined(_WIN32)
|
2023-10-23 23:51:21 +02:00
|
|
|
# define WIN32_LEAN_AND_MEAN
|
2023-10-23 21:32:30 +02:00
|
|
|
# include <Windows.h>
|
|
|
|
#elif defined(__ANDROID__)
|
|
|
|
# include "android/native_window.h"
|
|
|
|
#elif defined(__linux__)
|
|
|
|
# include "X11/Xlib.h"
|
|
|
|
# undef None
|
|
|
|
# undef Status
|
|
|
|
# undef LockMask
|
2024-04-21 20:18:33 +02:00
|
|
|
# undef Always
|
2024-04-25 16:51:04 +02:00
|
|
|
# undef Success
|
2023-10-23 21:32:30 +02:00
|
|
|
#endif
|
|
|
|
|
2023-02-20 04:27:35 +01:00
|
|
|
struct UltraThreadContext {
|
|
|
|
std::thread host_thread;
|
2024-04-25 08:57:06 +02:00
|
|
|
moodycamel::LightweightSemaphore running;
|
|
|
|
moodycamel::LightweightSemaphore initialized;
|
2023-02-20 04:27:35 +01:00
|
|
|
};
|
|
|
|
|
2023-11-13 01:40:02 +01:00
|
|
|
namespace ultramodern {
|
2023-02-20 04:27:35 +01:00
|
|
|
|
2023-10-23 21:32:30 +02:00
|
|
|
#if defined(_WIN32)
|
|
|
|
// Native HWND handle to the target window.
|
2023-11-04 17:45:22 +01:00
|
|
|
struct WindowHandle {
|
|
|
|
HWND window;
|
|
|
|
DWORD thread_id = (DWORD)-1;
|
|
|
|
auto operator<=>(const WindowHandle&) const = default;
|
|
|
|
};
|
2023-10-23 21:32:30 +02:00
|
|
|
#elif defined(__ANDROID__)
|
|
|
|
using WindowHandle = ANativeWindow*;
|
|
|
|
#elif defined(__linux__)
|
|
|
|
struct WindowHandle {
|
|
|
|
Display* display;
|
|
|
|
Window window;
|
2023-11-04 17:45:22 +01:00
|
|
|
auto operator<=>(const WindowHandle&) const = default;
|
2023-10-23 21:32:30 +02:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2023-02-20 04:27:35 +01:00
|
|
|
// We need a place in rdram to hold the PI handles, so pick an address in extended rdram
|
2024-01-13 07:39:08 +01:00
|
|
|
constexpr uint32_t rdram_size = 1024 * 1024 * 16; // 16MB to give extra room for anything custom
|
2023-02-20 04:27:35 +01:00
|
|
|
constexpr int32_t cart_handle = 0x80800000;
|
|
|
|
constexpr int32_t flash_handle = (int32_t)(cart_handle + sizeof(OSPiHandle));
|
|
|
|
constexpr uint32_t save_size = 1024 * 1024 / 8; // Maximum save size, 1Mbit for flash
|
|
|
|
|
2024-04-25 08:57:06 +02:00
|
|
|
// Initialization.
|
2024-03-04 03:00:49 +01:00
|
|
|
void preinit(uint8_t* rdram, WindowHandle window_handle);
|
2023-02-20 04:27:35 +01:00
|
|
|
void save_init();
|
2024-03-04 03:00:49 +01:00
|
|
|
void init_events(uint8_t* rdram, WindowHandle window_handle);
|
2023-02-20 04:27:35 +01:00
|
|
|
void init_timers(RDRAM_ARG1);
|
2024-04-25 08:57:06 +02:00
|
|
|
void init_thread_cleanup();
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
// Message queues.
|
|
|
|
void wait_for_external_message(RDRAM_ARG1);
|
|
|
|
|
|
|
|
// Thread scheduling.
|
|
|
|
void check_running_queue(RDRAM_ARG1);
|
2024-04-27 21:06:26 +02:00
|
|
|
void run_next_thread_and_wait(RDRAM_ARG1);
|
|
|
|
void resume_thread_and_wait(RDRAM_ARG OSThread* t);
|
2024-04-25 08:57:06 +02:00
|
|
|
void schedule_running_thread(RDRAM_ARG PTR(OSThread) t);
|
2024-04-27 21:06:26 +02:00
|
|
|
void cleanup_thread(UltraThreadContext* thread_context);
|
2024-01-13 07:39:08 +01:00
|
|
|
uint32_t permanent_thread_count();
|
|
|
|
uint32_t temporary_thread_count();
|
2024-04-25 08:57:06 +02:00
|
|
|
struct thread_terminated : std::exception {};
|
2023-07-07 20:21:06 +02:00
|
|
|
|
|
|
|
enum class ThreadPriority {
|
|
|
|
Low,
|
|
|
|
Normal,
|
|
|
|
High,
|
|
|
|
VeryHigh,
|
|
|
|
Critical
|
|
|
|
};
|
|
|
|
|
|
|
|
void set_native_thread_name(const std::string& name);
|
|
|
|
void set_native_thread_priority(ThreadPriority pri);
|
2023-02-20 04:27:35 +01:00
|
|
|
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();
|
|
|
|
uint32_t get_speed_multiplier();
|
2024-04-25 08:57:06 +02:00
|
|
|
|
|
|
|
// Time
|
2024-04-06 03:06:04 +02:00
|
|
|
std::chrono::high_resolution_clock::time_point get_start();
|
|
|
|
std::chrono::high_resolution_clock::duration time_since_start();
|
2024-03-21 07:46:08 +01:00
|
|
|
void measure_input_latency();
|
2024-04-06 03:06:04 +02:00
|
|
|
void sleep_milliseconds(uint32_t millis);
|
|
|
|
void sleep_until(const std::chrono::high_resolution_clock::time_point& time_point);
|
2023-06-23 00:29:55 +02:00
|
|
|
|
2024-04-25 08:57:06 +02:00
|
|
|
// Graphics
|
|
|
|
void get_window_size(uint32_t& width, uint32_t& height);
|
|
|
|
uint32_t get_target_framerate(uint32_t original);
|
|
|
|
uint32_t get_display_refresh_rate();
|
2024-04-27 06:02:10 +02:00
|
|
|
void load_shader_cache(std::span<const char> cache_data);
|
2024-04-25 08:57:06 +02:00
|
|
|
|
2023-06-23 00:29:55 +02:00
|
|
|
// Audio
|
2023-02-20 04:27:35 +01:00
|
|
|
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();
|
|
|
|
|
2023-06-23 00:29:55 +02:00
|
|
|
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;
|
2023-10-23 21:32:30 +02:00
|
|
|
get_samples_remaining_t* get_frames_remaining;
|
2023-06-23 00:29:55 +02:00
|
|
|
set_frequency_t* set_frequency;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Input
|
|
|
|
struct input_callbacks_t {
|
2023-12-13 08:06:56 +01:00
|
|
|
using poll_input_t = void(void);
|
2023-06-23 00:29:55 +02:00
|
|
|
using get_input_t = void(uint16_t*, float*, float*);
|
2024-03-04 16:12:52 +01:00
|
|
|
using set_rumble_t = void(bool);
|
2023-12-13 08:06:56 +01:00
|
|
|
poll_input_t* poll_input;
|
2023-06-23 00:29:55 +02:00
|
|
|
get_input_t* get_input;
|
2024-03-04 16:12:52 +01:00
|
|
|
set_rumble_t* set_rumble;
|
2023-06-23 00:29:55 +02:00
|
|
|
};
|
|
|
|
|
2023-10-23 21:32:30 +02:00
|
|
|
struct gfx_callbacks_t {
|
|
|
|
using gfx_data_t = void*;
|
|
|
|
using create_gfx_t = gfx_data_t();
|
|
|
|
using create_window_t = 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;
|
2023-02-20 04:27:35 +01:00
|
|
|
};
|
2023-11-05 20:34:20 +01:00
|
|
|
bool is_game_started();
|
2023-11-12 20:47:38 +01:00
|
|
|
void quit();
|
|
|
|
void join_event_threads();
|
2024-04-25 08:57:06 +02:00
|
|
|
void join_thread_cleaner_thread();
|
2023-10-24 00:01:29 +02:00
|
|
|
|
2023-11-13 01:40:02 +01:00
|
|
|
} // namespace ultramodern
|
2023-02-20 04:27:35 +01:00
|
|
|
|
|
|
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
|
|
|
2023-10-24 00:01:29 +02:00
|
|
|
#define debug_printf(...)
|
|
|
|
//#define debug_printf(...) printf(__VA_ARGS__);
|
2023-02-20 04:27:35 +01:00
|
|
|
|
|
|
|
#endif
|