1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
type Buffer = crate::double_buffer::DoubleBuffer<State>;

use crate::double_buffer::DoubleBuffer;
use crate::message_queue::{Message, MessageQueueElement};
use crate::sprite::Sprite;
use crate::state::{State, UnspecificState};
use const_env::from_env;
use rask_engine::resources::Resource;
use std::mem::size_of;

macro_rules! assert_env {
    ($var:expr) => {
        const _: &str = env!($var);
    };
}

assert_env!("WEE_ALLOC_STATIC_ARRAY_BACKEND_BYTES");

#[from_env]
/// The position of the stack.
pub const LOGIC_STACK: usize = 0;
assert_env!("LOGIC_STACK");

#[from_env]
/// The position of the stack.
pub const GRAPHICS_STACK: usize = 0;
assert_env!("GRAPHICS_STACK");

#[from_env]
/// The address of the Allocator structures
pub const ALLOCATOR: usize = 0;
assert_env!("ALLOCATOR");

#[from_env]
/// The graphics heap address
pub const GRAPHICS_HEAP: usize = 0;
assert_env!("GRAPHICS_HEAP");

#[from_env]
/// The address memory synchronization area.
/// It contains data needed for synchronization between main thread and logic thread.
pub const SYNCHRONIZATION_MEMORY: usize = 0;
assert_env!("SYNCHRONIZATION_MEMORY");

#[from_env]
/// Address of the internal resource library.
pub const RESOURCE_TABLE: usize = 0;
assert_env!("RESOURCE_TABLE");
#[from_env]
pub const RESOURCE_TABLE_SIZE: usize = 0;
assert_env!("RESOURCE_TABLE_SIZE");
pub const RESOURCE_TABLE_ELEMENT_COUNT: usize = RESOURCE_TABLE_SIZE / size_of::<Resource>();

#[from_env]
/// The address of the double buffer (size: target dependent)
pub const DOUBLE_BUFFER: usize = 0;
assert_env!("DOUBLE_BUFFER");
#[from_env]
pub const DOUBLE_BUFFER_SIZE: usize = 0;
assert_env!("DOUBLE_BUFFER_SIZE");
pub const DOUBLE_BUFFER_SPRITE_COUNT: usize =
    ((DOUBLE_BUFFER_SIZE as i64 - size_of::<DoubleBuffer<()>>() as i64) / 2
        - size_of::<UnspecificState<()>>() as i64) as usize
        / size_of::<Sprite>();

#[from_env]
/// Address of the event queue
pub const MESSAGE_QUEUE: usize = 0;
assert_env!("MESSAGE_QUEUE");
#[from_env]
pub const MESSAGE_QUEUE_SIZE: usize = 0;
assert_env!("MESSAGE_QUEUE_SIZE");
pub const MESSAGE_QUEUE_ELEMENT_COUNT: usize =
    MESSAGE_QUEUE_SIZE / size_of::<MessageQueueElement<Message>>();

#[from_env]
/// The logic heap address (size: 32MiB)
pub const LOGIC_HEAP: usize = 0;
assert_env!("LOGIC_HEAP");

pub fn get_double_buffer() -> &'static mut Buffer {
    unsafe { &mut *(DOUBLE_BUFFER as *mut Buffer) }
}

#[repr(C)]
pub struct GameState {
    pub player_x: f32,
    pub player_y: f32,
    pub player_state: i32,
}

#[repr(align(4))]
#[repr(C)]
pub struct SynchronizationMemory {
    /// time elapsed since logic thread initialisation in milliseconds
    pub elapsed_ms: i32,
    pub mouse: (i32, i32),
    pub canvas_size: (u32, u32),
    pub player: GameState,
    pub other: GameState,
    last_elapsed_ms: i32,
}

impl SynchronizationMemory {
    pub unsafe fn get() -> &'static Self {
        &*(SYNCHRONIZATION_MEMORY as *const Self)
    }
    pub unsafe fn get_mut() -> &'static mut Self {
        &mut *(SYNCHRONIZATION_MEMORY as *mut Self)
    }

    pub fn wait_for_main_thread_notify(&mut self) {
        self.last_elapsed_ms = self.elapsed_ms;
        while self.last_elapsed_ms == self.elapsed_ms {
            wait_until_wake_up_at((&mut self.elapsed_ms) as *mut i32)
        }
    }
}

extern "C" {
    #[link_name = "llvm.wasm.atomic.wait.i32"]
    /// see https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md#wait-and-notify-operators
    pub fn llvm_atomic_wait_i32(ptr: *mut i32, exp: i32, timeout: i64) -> i32;

    #[link_name = "llvm.wasm.atomic.notify"]
    /// see https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md#wait-and-notify-operators
    fn llvm_atomic_notify(ptr: *mut i32, cnt: i32) -> i32;
}

pub unsafe fn atomic_write_u8(ptr: *mut u8, v: u8) {
    (*(ptr as *mut core::sync::atomic::AtomicU8)).store(v, core::sync::atomic::Ordering::SeqCst)
}

pub unsafe fn atomic_read_u8(ptr: *const u8) -> u8 {
    (*(ptr as *const core::sync::atomic::AtomicU8)).load(core::sync::atomic::Ordering::SeqCst)
}

pub unsafe fn atomic_read_i32(ptr: *const i32) -> i32 {
    (*(ptr as *const core::sync::atomic::AtomicI32)).load(core::sync::atomic::Ordering::SeqCst)
}

pub unsafe fn atomic_write_u32(ptr: *mut u32, v: u32) {
    (*(ptr as *mut core::sync::atomic::AtomicU32)).store(v, core::sync::atomic::Ordering::SeqCst)
}

pub unsafe fn atomic_read_u32(ptr: *const u32) -> u32 {
    (*(ptr as *const core::sync::atomic::AtomicU32)).load(core::sync::atomic::Ordering::SeqCst)
}

pub fn wait_until_wake_up_at(ptr: *mut i32) {
    let res = unsafe { llvm_atomic_wait_i32(ptr, atomic_read_i32(ptr), -1) };
    debug_assert!(res == 0)
}

/// performs a notify at a given address and return the count of waiters
pub fn wake_up_at(ptr: *mut i32) -> bool {
    // Documented at https://tc39.es/ecma262/#sec-atomics.notify
    // and https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md#wait-and-notify-operators.
    // The notify function wakes all waiters up.

    (unsafe { llvm_atomic_notify(ptr, -1) }) > 0
}