尝试用预排序数组来替换小顶堆,效果不理想
如果真的要跑的好,还是需要对三巨头:DrawBackground/DrawWindow/DrawMerge进行改进,最好是批处理
This commit is contained in:
parent
c2cff2cc78
commit
4109905bc6
@ -29,7 +29,7 @@ enum Level {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
#if defined(NDEBUG)
|
#if defined(NDEBUG)
|
||||||
static constexpr int kLogMask = Info | Warn | Error | Fatal;
|
static constexpr int kLogMask = 0; //Info | Warn | Error | Fatal;
|
||||||
#else
|
#else
|
||||||
static constexpr int kLogMask = All;
|
static constexpr int kLogMask = All;
|
||||||
#endif
|
#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