尝试用预排序数组来替换小顶堆,效果不理想
如果真的要跑的好,还是需要对三巨头:DrawBackground/DrawWindow/DrawMerge进行改进,最好是批处理
This commit is contained in:
parent
c2cff2cc78
commit
4109905bc6
@ -29,7 +29,7 @@ enum Level {
|
||||
namespace detail {
|
||||
|
||||
#if defined(NDEBUG)
|
||||
static constexpr int kLogMask = Info | Warn | Error | Fatal;
|
||||
static constexpr int kLogMask = 0; //Info | Warn | Error | Fatal;
|
||||
#else
|
||||
static constexpr int kLogMask = All;
|
||||
#endif
|
||||
|
365
src/nba/include/nba/scheduler-arrimpl.hpp
Normal file
365
src/nba/include/nba/scheduler-arrimpl.hpp
Normal file
@ -0,0 +1,365 @@
|
||||
/*
|
||||
* Copyright (C) 2023 fleroviux
|
||||
*
|
||||
* Licensed under GPLv3 or any later version.
|
||||
* Refer to the included LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nba/log.hpp>
|
||||
#include <nba/common/compiler.hpp>
|
||||
#include <nba/integer.hpp>
|
||||
#include <nba/save_state.hpp>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
namespace nba::core {
|
||||
|
||||
struct Scheduler {
|
||||
template<class T>
|
||||
using EventMethod = void (T::*)();
|
||||
|
||||
template<class T>
|
||||
using EventMethodWithUserData = void (T::*)(u64);
|
||||
|
||||
enum class EventClass : u16 {
|
||||
// ARM
|
||||
ARM_ldm_usermode_conflict,
|
||||
|
||||
// PPU
|
||||
PPU_hdraw_vdraw,
|
||||
PPU_hblank_vdraw,
|
||||
PPU_hdraw_vblank,
|
||||
PPU_hblank_vblank,
|
||||
PPU_begin_sprite_fetch,
|
||||
PPU_update_vcount_flag,
|
||||
PPU_video_dma,
|
||||
PPU_latch_dispcnt,
|
||||
|
||||
PPU_hblank_irq,
|
||||
PPU_vblank_irq,
|
||||
PPU_vcount_irq,
|
||||
|
||||
// APU
|
||||
APU_mixer,
|
||||
APU_sequencer,
|
||||
APU_PSG1_generate,
|
||||
APU_PSG2_generate,
|
||||
APU_PSG3_generate,
|
||||
APU_PSG4_generate,
|
||||
|
||||
// IRQ controller
|
||||
IRQ_write_io,
|
||||
IRQ_update_ie_and_if,
|
||||
IRQ_update_irq_line,
|
||||
|
||||
// Timers
|
||||
TM_overflow,
|
||||
TM_write_reload,
|
||||
TM_write_control,
|
||||
|
||||
// DMA
|
||||
DMA_activated,
|
||||
|
||||
// Backup memory
|
||||
EEPROM_ready,
|
||||
|
||||
SIO_transfer_done,
|
||||
|
||||
KeyPad_Poll,
|
||||
|
||||
EndOfQueue,
|
||||
Count
|
||||
};
|
||||
|
||||
struct Event {
|
||||
u64 timestamp;
|
||||
|
||||
u64 UID() const { return uid; }
|
||||
|
||||
private:
|
||||
friend class Scheduler;
|
||||
int handle;
|
||||
u64 key;
|
||||
u64 uid;
|
||||
u64 user_data;
|
||||
EventClass event_class;
|
||||
};
|
||||
|
||||
Scheduler() {
|
||||
Register(EventClass::EndOfQueue, this, &Scheduler::EndOfQueue);
|
||||
|
||||
for(int i = 0; i < kMaxEvents; i++) {
|
||||
pool[i].handle = -1;
|
||||
heap[i] = nullptr;
|
||||
}
|
||||
|
||||
for(int i = 0; i < (int)EventClass::Count; i++) {
|
||||
callbacks[i] = [i](u64 user_data) {
|
||||
Assert(false, "Scheduler: unhandled event class: {}", i);
|
||||
};
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
heap_size = 0;
|
||||
timestamp_now = 0;
|
||||
next_uid = 1;
|
||||
|
||||
Add(std::numeric_limits<u64>::max(), EventClass::EndOfQueue);
|
||||
}
|
||||
|
||||
auto ALWAYS_INLINE GetTimestampNow() const -> u64 {
|
||||
return timestamp_now;
|
||||
}
|
||||
|
||||
void SetTimestampNow(u64 timestamp) {
|
||||
timestamp_now = timestamp;
|
||||
}
|
||||
|
||||
auto ALWAYS_INLINE GetTimestampTarget() const -> u64 {
|
||||
return heap[ heap_size-1 ]->timestamp;
|
||||
}
|
||||
|
||||
auto ALWAYS_INLINE GetRemainingCycleCount() const -> int {
|
||||
return int(GetTimestampTarget() - GetTimestampNow());
|
||||
}
|
||||
|
||||
void AddCycles(int cycles) {
|
||||
if( !cycles ) return;
|
||||
auto timestamp_next = timestamp_now + cycles;
|
||||
Step(timestamp_next);
|
||||
timestamp_now = timestamp_next;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Register(EventClass event_class, T* object, EventMethod<T> method, uint priority = 0) {
|
||||
callbacks[(int)event_class] = [object, method](u64 user_data) {
|
||||
(object->*method)();
|
||||
};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Register(EventClass event_class, T* object, EventMethodWithUserData<T> method, uint priority = 0) {
|
||||
callbacks[(int)event_class] = [object, method](u64 user_data) {
|
||||
(object->*method)(user_data);
|
||||
};
|
||||
}
|
||||
|
||||
auto Add(u64 delay, EventClass event_class, uint priority = 0, u64 user_data = 0) -> Event* {
|
||||
int n = heap_size++;
|
||||
//int p = Parent(n);
|
||||
|
||||
Assert(
|
||||
heap_size <= kMaxEvents,
|
||||
"Scheduler: reached maximum number of events."
|
||||
);
|
||||
|
||||
Assert(priority <= 3, "Scheduler: priority must be between 0 and 3.");
|
||||
|
||||
// auto event = heap[n];
|
||||
// event->timestamp = GetTimestampNow() + delay;
|
||||
// event->key = (event->timestamp << 2) | priority;
|
||||
// event->uid = next_uid++;
|
||||
// event->user_data = user_data;
|
||||
// event->event_class = event_class;
|
||||
u64 timestamp = GetTimestampNow() + delay;
|
||||
u64 evkey = (timestamp << 2) | priority;
|
||||
|
||||
// while(n != 0 && heap[p]->key > heap[n]->key) {
|
||||
// Swap(n, p);
|
||||
// n = p;
|
||||
// p = Parent(n);
|
||||
// }
|
||||
Event *in = nullptr;
|
||||
for( int i=0; i < kMaxEvents; ++i ){
|
||||
if( pool[i].handle < 0 ) in = pool + i;
|
||||
}
|
||||
|
||||
Assert( in != nullptr, "You should enlarge the kMaxEvent for queueing" );
|
||||
|
||||
if( heap_size > 1 ){
|
||||
auto next = &heap[ n ];
|
||||
auto head = &heap[0];
|
||||
for( auto curr = next-1; next != head; curr-- ){
|
||||
if( likely( (*curr)->key < evkey ) ){
|
||||
(*curr)->handle = n--;
|
||||
*next = *curr;
|
||||
next = curr;
|
||||
}
|
||||
else {
|
||||
*next = in;
|
||||
(*next)->handle = (*curr)->handle+1;
|
||||
(*next)->timestamp = timestamp;
|
||||
(*next)->key = evkey;
|
||||
(*next)->uid = next_uid++;
|
||||
(*next)->user_data = user_data;
|
||||
(*next)->event_class = event_class;
|
||||
return *next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto event = heap[0] = in;
|
||||
event->timestamp = timestamp;
|
||||
event->key = evkey;
|
||||
event->uid = next_uid++;
|
||||
event->user_data = user_data;
|
||||
event->event_class = event_class;
|
||||
event->handle = 0;
|
||||
return event;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
auto Add(u64 delay, T* object, EventMethod<T> method, uint priority = 0) -> Event* {
|
||||
return Add(delay, [object, method](int cycles_late) {
|
||||
(object->*method)(cycles_late);
|
||||
}, priority);
|
||||
}
|
||||
|
||||
void Cancel(Event* event) {
|
||||
Remove(event->handle);
|
||||
}
|
||||
|
||||
auto GetEventByUID(u64 uid) -> Event* {
|
||||
for(int i = 0; i < heap_size; i++) {
|
||||
auto event = heap[i];
|
||||
|
||||
if(event->uid == uid) {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void LoadState(SaveState const& state) {
|
||||
auto& ss_scheduler = state.scheduler;
|
||||
|
||||
for(int i = 0; i < ss_scheduler.event_count; i++) {
|
||||
auto& event = ss_scheduler.events[i];
|
||||
|
||||
u64 timestamp = event.key >> 2;
|
||||
int priority = event.key & 3;
|
||||
u64 uid = event.uid;
|
||||
u64 user_data = event.user_data;
|
||||
EventClass event_class = (EventClass)event.event_class;
|
||||
|
||||
// This event already was created on Scheduler::Reset()
|
||||
if(event_class == EventClass::EndOfQueue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Add(timestamp - state.timestamp, event_class, priority, user_data)->uid = uid;
|
||||
}
|
||||
|
||||
// This must happen after deserializing all events, because calling Add() modifies `next_uid`.
|
||||
next_uid = ss_scheduler.next_uid;
|
||||
}
|
||||
|
||||
void CopyState(SaveState& state) {
|
||||
auto& ss_scheduler = state.scheduler;
|
||||
|
||||
for(int i = 0; i < heap_size; i++) {
|
||||
auto event = heap[i];
|
||||
|
||||
ss_scheduler.events[i] = { event->key, event->uid, event->user_data, (u16)event->event_class };
|
||||
}
|
||||
|
||||
ss_scheduler.event_count = heap_size;
|
||||
ss_scheduler.next_uid = next_uid;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int kMaxEvents = 64;
|
||||
|
||||
static constexpr int Parent(int n) { return (n - 1) / 2; }
|
||||
static constexpr int LeftChild(int n) { return n * 2 + 1; }
|
||||
static constexpr int RightChild(int n) { return n * 2 + 2; }
|
||||
|
||||
Event pool[kMaxEvents];
|
||||
|
||||
// void ALWAYS_INLINE Swap(int i, int j) {
|
||||
// auto tmp = heap[i];
|
||||
// heap[i] = heap[j];
|
||||
// heap[j] = tmp;
|
||||
// heap[i]->handle = i;
|
||||
// heap[j]->handle = j;
|
||||
// }
|
||||
|
||||
void inline Step(u64 timestamp_next) {
|
||||
while( GetTimestampTarget() <= timestamp_next && heap_size > 0) {
|
||||
auto event = heap[ heap_size-1 ]; // heap[0];
|
||||
timestamp_now = event->timestamp;
|
||||
callbacks[(int)event->event_class](event->user_data);
|
||||
Remove(event->handle);
|
||||
}
|
||||
}
|
||||
|
||||
void Remove(int n) {
|
||||
auto out = heap[n];
|
||||
if( n+1 == heap_size ){
|
||||
heap[n] = nullptr;
|
||||
}
|
||||
else {
|
||||
auto end = &heap[ heap_size-1 ];
|
||||
auto out = heap[n];
|
||||
auto curr = &heap[n];
|
||||
for( auto next = curr+1; curr != end; ++next ){
|
||||
(*next)->handle = n++;
|
||||
*curr = *next;
|
||||
curr = next;
|
||||
}
|
||||
*end = nullptr;
|
||||
}
|
||||
--heap_size;
|
||||
out->handle = -1;
|
||||
// Swap(n, --heap_size);
|
||||
|
||||
// int p = Parent(n);
|
||||
// if(n != 0 && heap[p]->key > heap[n]->key) {
|
||||
// do {
|
||||
// Swap(n, p);
|
||||
// n = p;
|
||||
// p = Parent(n);
|
||||
// } while(n != 0 && heap[p]->key > heap[n]->key);
|
||||
// } else {
|
||||
// Heapify(n);
|
||||
// }
|
||||
}
|
||||
|
||||
// void Heapify(int n) {
|
||||
// int l = LeftChild(n);
|
||||
// int r = RightChild(n);
|
||||
|
||||
// if(l < heap_size && heap[l]->key < heap[n]->key) {
|
||||
// Swap(l, n);
|
||||
// Heapify(l);
|
||||
// }
|
||||
|
||||
// if(r < heap_size && heap[r]->key < heap[n]->key) {
|
||||
// Swap(r, n);
|
||||
// Heapify(r);
|
||||
// }
|
||||
// }
|
||||
|
||||
void EndOfQueue() {
|
||||
Assert(false, "Scheduler: reached end of the event queue.");
|
||||
}
|
||||
|
||||
Event* heap[kMaxEvents];
|
||||
int heap_size;
|
||||
u64 timestamp_now;
|
||||
u64 next_uid;
|
||||
|
||||
std::function<void(u64)> callbacks[(int)EventClass::Count];
|
||||
};
|
||||
|
||||
inline u64 GetEventUID(Scheduler::Event* event) {
|
||||
return event ? event->UID() : 0;
|
||||
}
|
||||
|
||||
} // namespace nba::core
|
Loading…
x
Reference in New Issue
Block a user