File size: 3,959 Bytes
6be3052 e76c9cf 12a0785 e76c9cf 12a0785 e76c9cf 12a0785 e76c9cf 12a0785 e76c9cf 84a3c5e e76c9cf 4031a30 e76c9cf 4031a30 e76c9cf 4031a30 e76c9cf 4031a30 84a3c5e e76c9cf 84a3c5e 12a0785 84a3c5e 12a0785 84a3c5e 4031a30 6be3052 | 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 | use super::message::{RenderMessage, TypedRenderCmd, COMMAND_TYPE_TYPED};
/// Render queue — manages a downward-growing arena buffer of draw commands.
///
/// Passed as `this` (ECX) to the original DrawSprite*/DrawRect/etc. enqueue
/// functions. The buffer area sits between offset 0x04 and the entry metadata
/// at 0x10000+. Entries are allocated from the end of the buffer downward.
///
/// Max 0x800 (2048) entries per frame.
#[repr(C)]
pub struct RenderQueue {
/// 0x00000: Buffer write offset (i32, decrements per allocation)
pub buffer_offset: i32,
/// 0x00004 - 0x10003: Buffer area for command entries (64KB)
pub _buffer: [u8; 0x10000],
/// 0x10004: Number of enqueued entries (max 0x800)
pub entry_count: u32,
/// 0x10008: Pointer array — entry_ptrs[N] points to the N-th command
pub entry_ptrs: [*mut u8; 0x800],
}
impl RenderQueue {
/// Allocate a command of type `T` from the downward-growing buffer.
///
/// Returns `None` if the queue is full (>= 0x800 entries or buffer exhausted).
/// The returned reference is registered in `entry_ptrs` and `entry_count` is incremented.
pub unsafe fn alloc<T>(&mut self) -> Option<&mut T> {
if self.entry_count >= 0x800 {
return None;
}
let new_offset = self.buffer_offset - core::mem::size_of::<T>() as i32;
if new_offset < 0 {
return None;
}
self.buffer_offset = new_offset;
let entry = &mut *(self._buffer.as_mut_ptr().add(new_offset as usize) as *mut T);
self.entry_ptrs[self.entry_count as usize] = entry as *mut T as *mut u8;
self.entry_count += 1;
Some(entry)
}
/// Allocate `size` bytes from the buffer WITHOUT registering an entry pointer.
///
/// Used for auxiliary data (vertex arrays, etc.) that will be referenced
/// from a typed command via an explicit pointer. Both the aux data and
/// the subsequent `push_typed()` command live in the same arena, freed
/// together on frame reset.
///
/// Unlike `alloc_raw()`, this does NOT consume an `entry_ptrs` slot or
/// increment `entry_count`.
pub unsafe fn alloc_aux(&mut self, size: usize) -> Option<*mut u8> {
let new_offset = self.buffer_offset - size as i32;
if new_offset < 0 {
return None;
}
self.buffer_offset = new_offset;
Some(self._buffer.as_mut_ptr().add(new_offset as usize))
}
/// Allocate `size` bytes from the downward-growing buffer.
///
/// Like `alloc<T>()` but for variable-size entries (e.g. legacy commands
/// still enqueued by unported WA.exe code).
/// Returns `None` if the queue is full or buffer exhausted.
pub unsafe fn alloc_raw(&mut self, size: usize) -> Option<*mut u8> {
if self.entry_count >= 0x800 {
return None;
}
let new_offset = self.buffer_offset - size as i32;
if new_offset < 0 {
return None;
}
self.buffer_offset = new_offset;
let ptr = self._buffer.as_mut_ptr().add(new_offset as usize);
self.entry_ptrs[self.entry_count as usize] = ptr;
self.entry_count += 1;
Some(ptr)
}
/// Enqueue a typed render message. Constant-time, zero allocation —
/// the message is stored inline in the existing per-frame buffer.
///
/// Returns `false` if the per-frame queue is full.
///
/// # Safety
///
/// The caller must ensure `self` points to a valid, live `RenderQueue`.
pub unsafe fn push_typed(&mut self, layer: u32, message: RenderMessage) -> bool {
match self.alloc::<TypedRenderCmd>() {
Some(slot) => {
*slot = TypedRenderCmd {
command_type: COMMAND_TYPE_TYPED,
layer,
message,
};
true
}
None => false,
}
}
}
|