mirror of https://github.com/inolen/redream.git
fix timestep to 60hz in emulator
don't render frames on-demand in PVR2, render in main loop remove framebuffers from backend
This commit is contained in:
parent
1eaac911d8
commit
0a1dbf1b66
|
@ -20,7 +20,7 @@ using namespace dreavm::system;
|
|||
DEFINE_string(bios, "dc_bios.bin", "Path to BIOS");
|
||||
DEFINE_string(flash, "dc_flash.bin", "Path to flash ROM");
|
||||
|
||||
Emulator::Emulator(System &sys) : sys_(sys) {
|
||||
Emulator::Emulator() {
|
||||
bios_ = new uint8_t[BIOS_SIZE];
|
||||
flash_ = new uint8_t[FLASH_SIZE];
|
||||
ram_ = new uint8_t[MAIN_RAM_M0_SIZE];
|
||||
|
@ -34,7 +34,7 @@ Emulator::Emulator(System &sys) : sys_(sys) {
|
|||
rt_frontend_ = new SH4Frontend(*memory_);
|
||||
// rt_backend_ = new InterpreterBackend(*memory_);
|
||||
rt_backend_ = new X64Backend(*memory_);
|
||||
rb_ = new GLBackend(sys);
|
||||
rb_ = new GLBackend(sys_);
|
||||
}
|
||||
|
||||
Emulator::~Emulator() {
|
||||
|
@ -55,9 +55,53 @@ Emulator::~Emulator() {
|
|||
delete rb_;
|
||||
}
|
||||
|
||||
void Emulator::Run(const char *path) {
|
||||
if (!Init()) {
|
||||
LOG_WARNING("Failed to initialize emulator");
|
||||
return;
|
||||
}
|
||||
|
||||
if (path) {
|
||||
LOG_INFO("Launching %s", path);
|
||||
|
||||
if ((strstr(path, ".bin") && !LaunchBIN(path)) ||
|
||||
(strstr(path, ".gdi") && !LaunchGDI(path))) {
|
||||
LOG_WARNING("Failed to launch %s", path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const std::chrono::nanoseconds step = HZ_TO_NANO(60);
|
||||
std::chrono::nanoseconds time_remaining = std::chrono::nanoseconds(0);
|
||||
auto current_time = std::chrono::high_resolution_clock::now();
|
||||
auto last_time = current_time;
|
||||
|
||||
while (true) {
|
||||
current_time = std::chrono::high_resolution_clock::now();
|
||||
time_remaining += current_time - last_time;
|
||||
last_time = current_time;
|
||||
|
||||
if (time_remaining < step) {
|
||||
continue;
|
||||
}
|
||||
|
||||
time_remaining -= step;
|
||||
|
||||
PumpEvents();
|
||||
|
||||
scheduler_->Tick(step);
|
||||
|
||||
RenderFrame();
|
||||
}
|
||||
}
|
||||
|
||||
bool Emulator::Init() {
|
||||
InitMemory();
|
||||
|
||||
if (!sys_.Init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rb_->Init()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -91,26 +135,6 @@ bool Emulator::Init() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Emulator::Launch(const char *path) {
|
||||
LOG_INFO("Launching %s", path);
|
||||
|
||||
if (strstr(path, ".bin")) {
|
||||
return LaunchBIN(path);
|
||||
} else if (strstr(path, ".gdi")) {
|
||||
return LaunchGDI(path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Emulator::Tick() {
|
||||
PumpEvents();
|
||||
|
||||
scheduler_->Tick();
|
||||
|
||||
RenderFrame();
|
||||
}
|
||||
|
||||
void Emulator::InitMemory() {
|
||||
memory_->Mount(BIOS_START, BIOS_END, MIRROR_MASK, bios_);
|
||||
memory_->Mount(FLASH_START, FLASH_END, MIRROR_MASK, flash_);
|
||||
|
@ -230,25 +254,33 @@ bool Emulator::LaunchGDI(const char *path) {
|
|||
void Emulator::PumpEvents() {
|
||||
SystemEvent ev;
|
||||
|
||||
sys_.PumpEvents();
|
||||
|
||||
while (sys_.PollEvent(&ev)) {
|
||||
if (ev.type == SE_KEY) {
|
||||
// let the profiler take a stab at the input first
|
||||
if (!Profiler::HandleInput(ev.key.code, ev.key.value)) {
|
||||
// debug tracing
|
||||
if (ev.key.code == K_F2) {
|
||||
if (ev.key.value) {
|
||||
holly_->pvr().ToggleTracing();
|
||||
switch (ev.type) {
|
||||
case SE_KEY: {
|
||||
// let the profiler take a stab at the input first
|
||||
if (!Profiler::HandleInput(ev.key.code, ev.key.value)) {
|
||||
// debug tracing
|
||||
if (ev.key.code == K_F2) {
|
||||
if (ev.key.value) {
|
||||
holly_->pvr().ToggleTracing();
|
||||
}
|
||||
}
|
||||
// else, forward to maple
|
||||
else {
|
||||
holly_->maple().HandleInput(0, ev.key.code, ev.key.value);
|
||||
}
|
||||
}
|
||||
// else, forward to maple
|
||||
else {
|
||||
holly_->maple().HandleInput(0, ev.key.code, ev.key.value);
|
||||
}
|
||||
}
|
||||
} else if (ev.type == SE_MOUSEMOVE) {
|
||||
Profiler::HandleMouseMove(ev.mousemove.x, ev.mousemove.y);
|
||||
} else if (ev.type == SE_RESIZE) {
|
||||
rb_->SetFramebufferSize(FB_DEFAULT, ev.resize.width, ev.resize.height);
|
||||
} break;
|
||||
|
||||
case SE_MOUSEMOVE: {
|
||||
Profiler::HandleMouseMove(ev.mousemove.x, ev.mousemove.y);
|
||||
} break;
|
||||
|
||||
case SE_RESIZE: {
|
||||
rb_->ResizeVideo(ev.resize.width, ev.resize.height);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -256,14 +288,12 @@ void Emulator::PumpEvents() {
|
|||
void Emulator::RenderFrame() {
|
||||
rb_->BeginFrame();
|
||||
|
||||
// render latest TA output
|
||||
rb_->RenderFramebuffer(FB_TILE_ACCELERATOR);
|
||||
holly_->pvr().RenderLastFrame();
|
||||
|
||||
// render stats
|
||||
char stats[512];
|
||||
snprintf(stats, sizeof(stats), "%.2f%%, %.2f fps, %.2f vbps",
|
||||
scheduler_->perf(), holly_->pvr().fps(), holly_->pvr().vbps());
|
||||
// LOG_EVERY_N(INFO, 10) << stats;
|
||||
snprintf(stats, sizeof(stats), "%.2f fps, %.2f vbps", holly_->pvr().fps(),
|
||||
holly_->pvr().vbps());
|
||||
rb_->RenderText2D(0, 0, 12.0f, 0xffffffff, stats);
|
||||
|
||||
// render profiler
|
||||
|
|
|
@ -14,14 +14,13 @@ namespace emu {
|
|||
|
||||
class Emulator {
|
||||
public:
|
||||
Emulator(system::System &sys);
|
||||
Emulator();
|
||||
~Emulator();
|
||||
|
||||
bool Init();
|
||||
bool Launch(const char *path);
|
||||
void Tick();
|
||||
void Run(const char *path);
|
||||
|
||||
private:
|
||||
bool Init();
|
||||
void InitMemory();
|
||||
bool LoadBios(const char *path);
|
||||
bool LoadFlash(const char *path);
|
||||
|
@ -32,7 +31,7 @@ class Emulator {
|
|||
void PumpEvents();
|
||||
void RenderFrame();
|
||||
|
||||
system::System &sys_;
|
||||
system::System sys_;
|
||||
emu::Scheduler *scheduler_;
|
||||
emu::Memory *memory_;
|
||||
cpu::Runtime *runtime_;
|
||||
|
|
|
@ -4,21 +4,14 @@
|
|||
|
||||
using namespace dreavm::emu;
|
||||
|
||||
Scheduler::Scheduler()
|
||||
: next_timer_handle_(0),
|
||||
timeslice_(HZ_TO_NANO(60)),
|
||||
base_time_(),
|
||||
next_time_(base_time_ + timeslice_),
|
||||
tick_deltas_(),
|
||||
tick_idx_(0),
|
||||
perf_(0.0f) {}
|
||||
Scheduler::Scheduler() : next_timer_handle_(0), base_time_() {}
|
||||
|
||||
DeviceHandle Scheduler::AddDevice(Device *device) {
|
||||
devices_.push_back(DeviceInfo(device));
|
||||
return static_cast<DeviceHandle>(devices_.size() - 1);
|
||||
}
|
||||
|
||||
TimerHandle Scheduler::AddTimer(std::chrono::nanoseconds period,
|
||||
TimerHandle Scheduler::AddTimer(const std::chrono::nanoseconds &period,
|
||||
TimerCallback callback) {
|
||||
TimerHandle handle = next_timer_handle_++;
|
||||
|
||||
|
@ -28,7 +21,7 @@ TimerHandle Scheduler::AddTimer(std::chrono::nanoseconds period,
|
|||
}
|
||||
|
||||
void Scheduler::AdjustTimer(TimerHandle handle,
|
||||
std::chrono::nanoseconds period) {
|
||||
const std::chrono::nanoseconds &period) {
|
||||
std::set<Timer>::iterator it;
|
||||
|
||||
for (it = timers_.begin(); it != timers_.end(); ++it) {
|
||||
|
@ -69,14 +62,14 @@ void Scheduler::RemoveTimer(TimerHandle handle) {
|
|||
timers_.erase(it);
|
||||
}
|
||||
|
||||
void Scheduler::Tick() {
|
||||
UpdatePerf();
|
||||
void Scheduler::Tick(const std::chrono::nanoseconds &delta) {
|
||||
auto next_time = base_time_ + delta;
|
||||
|
||||
while (base_time_ < next_time_) {
|
||||
std::chrono::high_resolution_clock::time_point target_time = next_time_;
|
||||
while (base_time_ < next_time) {
|
||||
std::chrono::high_resolution_clock::time_point target_time = next_time;
|
||||
|
||||
// run devices up until the next timer expiration
|
||||
if (timers_.size() && timers_.begin()->expire < next_time_) {
|
||||
if (timers_.size() && timers_.begin()->expire < next_time) {
|
||||
target_time = timers_.begin()->expire;
|
||||
}
|
||||
|
||||
|
@ -112,21 +105,4 @@ void Scheduler::Tick() {
|
|||
timers_.insert(t);
|
||||
}
|
||||
}
|
||||
|
||||
next_time_ = base_time_ + timeslice_;
|
||||
}
|
||||
|
||||
void Scheduler::UpdatePerf() {
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
auto delta =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(now - last_tick_);
|
||||
tick_deltas_[tick_idx_] = delta.count();
|
||||
tick_idx_ = (tick_idx_ + 1) % NUM_TICK_DELTAS;
|
||||
last_tick_ = now;
|
||||
|
||||
int64_t total = 0;
|
||||
for (int i = 0; i < NUM_TICK_DELTAS; i++) {
|
||||
total += tick_deltas_[i];
|
||||
}
|
||||
perf_ = ((timeslice_.count() * NUM_TICK_DELTAS) / (float)total) * 100.0f;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace emu {
|
|||
|
||||
typedef std::function<void()> TimerCallback;
|
||||
|
||||
enum { INVALID_HANDLE = -1, NS_PER_SEC = 1000000000, NUM_TICK_DELTAS = 10 };
|
||||
enum { INVALID_HANDLE = -1, NS_PER_SEC = 1000000000, NS_PER_MS = 1000000 };
|
||||
|
||||
static inline std::chrono::nanoseconds HZ_TO_NANO(int64_t hz) {
|
||||
return std::chrono::nanoseconds(NS_PER_SEC / hz);
|
||||
|
@ -51,29 +51,19 @@ class Scheduler {
|
|||
public:
|
||||
Scheduler();
|
||||
|
||||
float perf() const { return perf_; }
|
||||
|
||||
DeviceHandle AddDevice(Device *device);
|
||||
TimerHandle AddTimer(std::chrono::nanoseconds period, TimerCallback callback);
|
||||
void AdjustTimer(TimerHandle handle, std::chrono::nanoseconds period);
|
||||
TimerHandle AddTimer(const std::chrono::nanoseconds &period,
|
||||
TimerCallback callback);
|
||||
void AdjustTimer(TimerHandle handle, const std::chrono::nanoseconds &period);
|
||||
void RemoveTimer(TimerHandle handle);
|
||||
void Tick();
|
||||
void Tick(const std::chrono::nanoseconds &delta);
|
||||
|
||||
private:
|
||||
void UpdatePerf();
|
||||
|
||||
std::vector<DeviceInfo> devices_;
|
||||
std::set<Timer> timers_;
|
||||
|
||||
TimerHandle next_timer_handle_;
|
||||
std::chrono::nanoseconds timeslice_;
|
||||
std::chrono::high_resolution_clock::time_point base_time_;
|
||||
std::chrono::high_resolution_clock::time_point next_time_;
|
||||
|
||||
int64_t tick_deltas_[NUM_TICK_DELTAS];
|
||||
int tick_idx_;
|
||||
std::chrono::high_resolution_clock::time_point last_tick_;
|
||||
float perf_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ PVR2::PVR2(Scheduler &scheduler, Memory &memory, Holly &holly)
|
|||
memory_(memory),
|
||||
holly_(holly),
|
||||
ta_(memory, holly, *this),
|
||||
rb_(nullptr),
|
||||
line_timer_(INVALID_HANDLE),
|
||||
current_scanline_(0),
|
||||
fps_(0),
|
||||
|
@ -26,21 +25,20 @@ PVR2::~PVR2() {
|
|||
}
|
||||
|
||||
bool PVR2::Init(Backend *rb) {
|
||||
rb_ = rb;
|
||||
|
||||
InitMemory();
|
||||
|
||||
if (!ta_.Init(rb_)) {
|
||||
if (!ta_.Init(rb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reset();
|
||||
ReconfigureVideoOutput();
|
||||
ReconfigureSPG();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PVR2::RenderLastFrame() { ta_.RenderLastContext(); }
|
||||
|
||||
void PVR2::ToggleTracing() { ta_.ToggleTracing(); }
|
||||
|
||||
namespace dreavm {
|
||||
|
@ -121,6 +119,8 @@ void PVR2::WriteRegister(void *ctx, uint32_t addr, uint32_t value) {
|
|||
if (reset_ta) {
|
||||
pvr->ta_.SoftReset();
|
||||
}
|
||||
} else if (reg.offset == TA_LIST_INIT_OFFSET) {
|
||||
pvr->ta_.InitContext(pvr->TA_ISP_BASE.base_address);
|
||||
} else if (reg.offset == STARTRENDER_OFFSET) {
|
||||
{
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
|
@ -130,13 +130,9 @@ void PVR2::WriteRegister(void *ctx, uint32_t addr, uint32_t value) {
|
|||
pvr->fps_ = 1000000000.0f / delta.count();
|
||||
}
|
||||
|
||||
pvr->ta_.RenderContext(pvr->PARAM_BASE.base_address);
|
||||
} else if (reg.offset == SPG_CONTROL_OFFSET) {
|
||||
pvr->ReconfigureVideoOutput();
|
||||
pvr->ta_.SaveLastContext(pvr->PARAM_BASE.base_address);
|
||||
} else if (reg.offset == SPG_LOAD_OFFSET || reg.offset == FB_R_CTRL_OFFSET) {
|
||||
pvr->ReconfigureSPG();
|
||||
} else if (reg.offset == TA_LIST_INIT_OFFSET) {
|
||||
pvr->ta_.InitContext(pvr->TA_ISP_BASE.base_address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,25 +165,6 @@ void PVR2::Reset() {
|
|||
#undef PVR_REG
|
||||
}
|
||||
|
||||
void PVR2::ReconfigureVideoOutput() {
|
||||
int render_width = 320;
|
||||
int render_height = 240;
|
||||
|
||||
// interlaced and VGA mode both render at full resolution
|
||||
if (SPG_CONTROL.interlace || (!SPG_CONTROL.NTSC && !SPG_CONTROL.PAL)) {
|
||||
render_width = 640;
|
||||
render_height = 480;
|
||||
}
|
||||
|
||||
LOG_INFO(
|
||||
"ReconfigureVideoOutput width %d, height %d, interlace %d, NTSC %d, PAL "
|
||||
"%d",
|
||||
render_width, render_height, SPG_CONTROL.interlace, SPG_CONTROL.NTSC,
|
||||
SPG_CONTROL.PAL);
|
||||
|
||||
ta_.ResizeVideo(render_width, render_height);
|
||||
}
|
||||
|
||||
void PVR2::ReconfigureSPG() {
|
||||
static const int PIXEL_CLOCK = 27000000; // 27mhz
|
||||
|
||||
|
|
|
@ -213,6 +213,7 @@ class PVR2 {
|
|||
float vbps() { return vbps_; }
|
||||
|
||||
bool Init(renderer::Backend *rb);
|
||||
void RenderLastFrame();
|
||||
void ToggleTracing();
|
||||
|
||||
private:
|
||||
|
@ -227,7 +228,6 @@ class PVR2 {
|
|||
|
||||
void InitMemory();
|
||||
void Reset();
|
||||
void ReconfigureVideoOutput();
|
||||
void ReconfigureSPG();
|
||||
void LineClockUpdate();
|
||||
|
||||
|
@ -235,7 +235,6 @@ class PVR2 {
|
|||
emu::Memory &memory_;
|
||||
Holly &holly_;
|
||||
TileAccelerator ta_;
|
||||
renderer::Backend *rb_;
|
||||
|
||||
emu::TimerHandle line_timer_;
|
||||
uint32_t current_scanline_;
|
||||
|
|
|
@ -269,7 +269,8 @@ TileAccelerator::TileAccelerator(Memory &memory, Holly &holly, PVR2 &pvr)
|
|||
holly_(holly),
|
||||
pvr_(pvr),
|
||||
texcache_(*this),
|
||||
renderer_(texcache_) {}
|
||||
tile_renderer_(texcache_),
|
||||
last_context_(&scratch_context_) {}
|
||||
|
||||
TileAccelerator::~TileAccelerator() {
|
||||
while (contexts_.size()) {
|
||||
|
@ -290,14 +291,6 @@ bool TileAccelerator::Init(Backend *rb) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void TileAccelerator::ResizeVideo(int width, int height) {
|
||||
rb_->SetFramebufferSize(FB_TILE_ACCELERATOR, width, height);
|
||||
|
||||
if (trace_writer_) {
|
||||
trace_writer_->WriteResizeVideo(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void TileAccelerator::SoftReset() {
|
||||
// FIXME what are we supposed to do here?
|
||||
}
|
||||
|
@ -360,24 +353,29 @@ void TileAccelerator::WriteContext(uint32_t addr, uint32_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
void TileAccelerator::RenderContext(uint32_t addr) {
|
||||
// get context and update with PVR state
|
||||
TileContext *tactx = GetContext(addr);
|
||||
void TileAccelerator::SaveLastContext(uint32_t addr) {
|
||||
// swap context with last context to be delayed rendered
|
||||
auto it = FindContext(addr);
|
||||
TileContext *tmp = last_context_;
|
||||
last_context_ = it->second;
|
||||
it->second = tmp;
|
||||
|
||||
WritePVRState(tactx);
|
||||
WriteBackgroundState(tactx);
|
||||
// save PVR state to context
|
||||
WritePVRState(last_context_);
|
||||
WriteBackgroundState(last_context_);
|
||||
|
||||
// do the actual rendering
|
||||
renderer_.RenderContext(tactx, rb_);
|
||||
|
||||
// let holly know the rendering is complete
|
||||
// tell holly that rendering is complete
|
||||
holly_.RequestInterrupt(HOLLY_INTC_PCEOVINT);
|
||||
holly_.RequestInterrupt(HOLLY_INTC_PCEOIINT);
|
||||
holly_.RequestInterrupt(HOLLY_INTC_PCEOTINT);
|
||||
}
|
||||
|
||||
void TileAccelerator::RenderLastContext() {
|
||||
tile_renderer_.RenderContext(last_context_, rb_);
|
||||
|
||||
// add render to trace
|
||||
if (trace_writer_) {
|
||||
trace_writer_->WriteRenderContext(tactx);
|
||||
trace_writer_->WriteRenderContext(last_context_);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,11 +393,6 @@ void TileAccelerator::ToggleTracing() {
|
|||
|
||||
LOG_INFO("Begin tracing to %s", filename);
|
||||
|
||||
// write out the initial framebuffer size
|
||||
int width, height;
|
||||
rb_->GetFramebufferSize(FB_TILE_ACCELERATOR, &width, &height);
|
||||
trace_writer_->WriteResizeVideo(width, height);
|
||||
|
||||
// clear the texture cache, so the next render will write out insert
|
||||
// texture commands for any textures in use
|
||||
texcache_.Clear();
|
||||
|
@ -456,18 +449,25 @@ void TileAccelerator::InitMemory() {
|
|||
&TileAccelerator::WriteTexture<uint32_t>, nullptr);
|
||||
}
|
||||
|
||||
TileContext *TileAccelerator::GetContext(uint32_t addr) {
|
||||
auto it = contexts_.find(addr);
|
||||
TileContextIterator TileAccelerator::FindContext(uint32_t addr) {
|
||||
TileContextIterator it = contexts_.find(addr);
|
||||
|
||||
if (it != contexts_.end()) {
|
||||
return it->second;
|
||||
// add context if it doesn't exist
|
||||
if (it == contexts_.end()) {
|
||||
TileContext *ctx = new TileContext();
|
||||
auto result = contexts_.insert(std::make_pair(addr, ctx));
|
||||
it = result.first;
|
||||
}
|
||||
|
||||
TileContext *ctx = new TileContext();
|
||||
ctx->addr = addr;
|
||||
// set context address
|
||||
it->second->addr = addr;
|
||||
|
||||
auto result = contexts_.insert(std::make_pair(addr, ctx));
|
||||
return result.first->second;
|
||||
return it;
|
||||
}
|
||||
|
||||
TileContext *TileAccelerator::GetContext(uint32_t addr) {
|
||||
TileContextIterator it = FindContext(addr);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void TileAccelerator::WritePVRState(TileContext *tactx) {
|
||||
|
@ -484,6 +484,18 @@ void TileAccelerator::WritePVRState(TileContext *tactx) {
|
|||
|
||||
// texture palette pixel format
|
||||
tactx->pal_pxl_format = pvr_.PAL_RAM_CTRL.pixel_format;
|
||||
|
||||
// write out video width to help with unprojecting the screen space
|
||||
// coordinates
|
||||
if (pvr_.SPG_CONTROL.interlace ||
|
||||
(!pvr_.SPG_CONTROL.NTSC && !pvr_.SPG_CONTROL.PAL)) {
|
||||
// interlaced and VGA mode both render at full resolution
|
||||
tactx->video_width = 640;
|
||||
tactx->video_height = 480;
|
||||
} else {
|
||||
tactx->video_width = 320;
|
||||
tactx->video_height = 240;
|
||||
}
|
||||
}
|
||||
|
||||
void TileAccelerator::WriteBackgroundState(TileContext *tactx) {
|
||||
|
|
|
@ -452,6 +452,8 @@ struct TileContext {
|
|||
bool autosort;
|
||||
int stride;
|
||||
int pal_pxl_format;
|
||||
int video_width;
|
||||
int video_height;
|
||||
ISP_TSP bg_isp;
|
||||
TSP bg_tsp;
|
||||
TCW bg_tcw;
|
||||
|
@ -486,6 +488,9 @@ class TileTextureCache : public TextureCache {
|
|||
std::unordered_map<uint32_t, renderer::TextureHandle> textures_;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<uint32_t, TileContext *> TileContextMap;
|
||||
typedef TileContextMap::iterator TileContextIterator;
|
||||
|
||||
class TileAccelerator {
|
||||
friend class TileTextureCache;
|
||||
|
||||
|
@ -499,12 +504,11 @@ class TileAccelerator {
|
|||
|
||||
bool Init(renderer::Backend *rb);
|
||||
|
||||
void ResizeVideo(int width, int height);
|
||||
|
||||
void SoftReset();
|
||||
void InitContext(uint32_t addr);
|
||||
void WriteContext(uint32_t addr, uint32_t value);
|
||||
void RenderContext(uint32_t addr);
|
||||
void SaveLastContext(uint32_t addr);
|
||||
void RenderLastContext();
|
||||
|
||||
void ToggleTracing();
|
||||
|
||||
|
@ -515,6 +519,7 @@ class TileAccelerator {
|
|||
static void WriteTexture(void *ctx, uint32_t addr, T value);
|
||||
|
||||
void InitMemory();
|
||||
TileContextIterator FindContext(uint32_t addr);
|
||||
TileContext *GetContext(uint32_t addr);
|
||||
void WritePVRState(TileContext *tactx);
|
||||
void WriteBackgroundState(TileContext *tactx);
|
||||
|
@ -525,8 +530,10 @@ class TileAccelerator {
|
|||
renderer::Backend *rb_;
|
||||
|
||||
TileTextureCache texcache_;
|
||||
TileRenderer renderer_;
|
||||
std::unordered_map<uint32_t, TileContext *> contexts_;
|
||||
TileRenderer tile_renderer_;
|
||||
TileContextMap contexts_;
|
||||
TileContext scratch_context_;
|
||||
TileContext *last_context_;
|
||||
|
||||
std::unique_ptr<trace::TraceWriter> trace_writer_;
|
||||
};
|
||||
|
|
|
@ -113,8 +113,6 @@ void TileRenderer::RenderContext(const TileContext *tactx, Backend *rb) {
|
|||
|
||||
Reset();
|
||||
|
||||
rb->GetFramebufferSize(FB_TILE_ACCELERATOR, &width_, &height_);
|
||||
|
||||
ParseBackground(tactx);
|
||||
|
||||
while (data < end) {
|
||||
|
@ -163,12 +161,10 @@ void TileRenderer::RenderContext(const TileContext *tactx, Backend *rb) {
|
|||
data += TileAccelerator::GetParamSize(pcw, vertex_type_);
|
||||
}
|
||||
|
||||
// LOG_INFO("StartRender %d surfs, %d verts, %d bytes", num_surfs_, num_verts,
|
||||
// tactx->size);
|
||||
// LOG_INFO("StartRender %d surfs, %d verts, %d bytes", num_surfs_,
|
||||
// num_verts_, tactx->size);
|
||||
|
||||
const Eigen::Matrix4f &projection = GetProjectionMatrix();
|
||||
rb->BindFramebuffer(FB_TILE_ACCELERATOR);
|
||||
rb->Clear(0.1f, 0.39f, 0.88f, 1.0f);
|
||||
const Eigen::Matrix4f &projection = GetProjectionMatrix(tactx);
|
||||
rb->RenderSurfaces(projection, surfs_, num_surfs_, verts_, num_verts_,
|
||||
sorted_surfs_);
|
||||
}
|
||||
|
@ -328,13 +324,13 @@ void TileRenderer::ParseBackground(const TileContext *tactx) {
|
|||
// override the xyz values supplied by ISP_BACKGND_T. while the hardware docs
|
||||
// act like the should be correct, they're most definitely not in most cases
|
||||
verts[0]->xyz[0] = 0.0f;
|
||||
verts[0]->xyz[1] = (float)height_;
|
||||
verts[0]->xyz[1] = (float)tactx->video_height;
|
||||
verts[0]->xyz[2] = tactx->bg_depth;
|
||||
verts[1]->xyz[0] = 0.0f;
|
||||
verts[1]->xyz[1] = 0.0f;
|
||||
verts[1]->xyz[2] = tactx->bg_depth;
|
||||
verts[2]->xyz[0] = (float)width_;
|
||||
verts[2]->xyz[1] = (float)height_;
|
||||
verts[2]->xyz[0] = (float)tactx->video_width;
|
||||
verts[2]->xyz[1] = (float)tactx->video_height;
|
||||
verts[2]->xyz[2] = tactx->bg_depth;
|
||||
|
||||
// 4th vertex isn't supplied, fill it out automatically
|
||||
|
@ -657,7 +653,7 @@ void TileRenderer::ParseEndOfList(const TileContext *tactx) {
|
|||
// projection on the vertices as they're already perspective correct, the
|
||||
// renderer backend will have to deal with setting the W component of each
|
||||
// in order to perspective correct the texture mapping.
|
||||
Eigen::Matrix4f TileRenderer::GetProjectionMatrix() {
|
||||
Eigen::Matrix4f TileRenderer::GetProjectionMatrix(const TileContext *tactx) {
|
||||
float znear = std::numeric_limits<float>::min();
|
||||
float zfar = std::numeric_limits<float>::max();
|
||||
|
||||
|
@ -682,8 +678,8 @@ Eigen::Matrix4f TileRenderer::GetProjectionMatrix() {
|
|||
|
||||
// convert from window space coordinates into clip space
|
||||
Eigen::Matrix4f p = Eigen::Matrix4f::Identity();
|
||||
p(0, 0) = 2.0f / (float)width_;
|
||||
p(1, 1) = -2.0f / (float)height_;
|
||||
p(0, 0) = 2.0f / (float)tactx->video_width;
|
||||
p(1, 1) = -2.0f / (float)tactx->video_height;
|
||||
p(0, 3) = -1.0f;
|
||||
p(1, 3) = 1.0f;
|
||||
p(2, 2) = (-znear - zfar) / zdepth;
|
||||
|
|
|
@ -58,7 +58,7 @@ class TileRenderer {
|
|||
void ParseVertexParam(const TileContext *tactx, renderer::Backend *rb,
|
||||
const VertexParam *param);
|
||||
void ParseEndOfList(const TileContext *tactx);
|
||||
Eigen::Matrix4f GetProjectionMatrix();
|
||||
Eigen::Matrix4f GetProjectionMatrix(const TileContext *tactx);
|
||||
|
||||
renderer::TextureHandle RegisterTexture(const TileContext *tactx,
|
||||
renderer::Backend *rb, const TSP &tsp,
|
||||
|
@ -82,7 +82,6 @@ class TileRenderer {
|
|||
// current render state
|
||||
renderer::Surface surfs_[MAX_SURFACES];
|
||||
renderer::Vertex verts_[MAX_VERTICES];
|
||||
int width_, height_;
|
||||
int num_surfs_;
|
||||
int num_verts_;
|
||||
int sorted_surfs_[MAX_SURFACES];
|
||||
|
|
52
src/main.cc
52
src/main.cc
|
@ -1,5 +1,4 @@
|
|||
#include <stdio.h>
|
||||
#include "system/system.h"
|
||||
#include "emu/emulator.h"
|
||||
#include "trace/trace_viewer.h"
|
||||
|
||||
|
@ -29,60 +28,19 @@ void InitFlags(int *argc, char ***argv) {
|
|||
|
||||
void ShutdownFlags() { google::ShutDownCommandLineFlags(); }
|
||||
|
||||
void RunEmulator(const char *launch) {
|
||||
System sys;
|
||||
Emulator emu(sys);
|
||||
|
||||
if (!sys.Init()) {
|
||||
LOG_FATAL("Failed to initialize window.");
|
||||
}
|
||||
|
||||
if (!emu.Init()) {
|
||||
LOG_FATAL("Failed to initialize emulator.");
|
||||
}
|
||||
|
||||
if (launch && !emu.Launch(launch)) {
|
||||
LOG_FATAL("Failed to load %s", launch);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
sys.Tick();
|
||||
emu.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
void RunTraceViewer(const char *trace) {
|
||||
System sys;
|
||||
TraceViewer tracer(sys);
|
||||
|
||||
if (!sys.Init()) {
|
||||
LOG_FATAL("Failed to initialize window.");
|
||||
}
|
||||
|
||||
if (!tracer.Init()) {
|
||||
LOG_FATAL("Failed to initialize tracer.");
|
||||
}
|
||||
|
||||
if (!tracer.Load(trace)) {
|
||||
LOG_FATAL("Failed to load %s", trace);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
sys.Tick();
|
||||
tracer.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
EnsureAppDirExists();
|
||||
|
||||
InitFlags(&argc, &argv);
|
||||
|
||||
const char *load = argc > 1 ? argv[1] : nullptr;
|
||||
|
||||
if (load && strstr(load, ".trace")) {
|
||||
RunTraceViewer(load);
|
||||
TraceViewer tracer;
|
||||
tracer.Run(load);
|
||||
} else {
|
||||
RunEmulator(load);
|
||||
Emulator emu;
|
||||
emu.Run(load);
|
||||
}
|
||||
|
||||
ShutdownFlags();
|
||||
|
|
|
@ -13,11 +13,6 @@ enum FilterMode { //
|
|||
FILTER_BILINEAR
|
||||
};
|
||||
|
||||
enum Framebuffer { //
|
||||
FB_DEFAULT,
|
||||
FB_TILE_ACCELERATOR
|
||||
};
|
||||
|
||||
enum PixelFormat {
|
||||
PXL_INVALID,
|
||||
PXL_RGBA5551,
|
||||
|
@ -110,18 +105,14 @@ class Backend {
|
|||
|
||||
virtual bool Init() = 0;
|
||||
|
||||
virtual void ResizeVideo(int width, int height) = 0;
|
||||
|
||||
virtual TextureHandle RegisterTexture(PixelFormat format, FilterMode filter,
|
||||
bool gen_mipmaps, int width, int height,
|
||||
const uint8_t *buffer) = 0;
|
||||
virtual void FreeTexture(TextureHandle handle) = 0;
|
||||
|
||||
virtual void SetFramebufferSize(Framebuffer fb, int width, int height) = 0;
|
||||
virtual void GetFramebufferSize(Framebuffer fb, int *width, int *height) = 0;
|
||||
|
||||
virtual void BeginFrame() = 0;
|
||||
virtual void BindFramebuffer(Framebuffer fb) = 0;
|
||||
virtual void Clear(float r, float g, float b, float a) = 0;
|
||||
virtual void RenderFramebuffer(Framebuffer fb) = 0;
|
||||
virtual void RenderText2D(int x, int y, float point_size, uint32_t color,
|
||||
const char *text) = 0;
|
||||
virtual void RenderBox2D(int x0, int y0, int x1, int y1, uint32_t color,
|
||||
|
|
|
@ -53,23 +53,20 @@ static GLenum blend_funcs[] = {
|
|||
GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR};
|
||||
|
||||
GLBackend::GLBackend(GLContext &ctx)
|
||||
: ctx_(ctx), textures_{0}, fb_ta_(0), num_verts2d_(0), num_surfs2d_(0) {}
|
||||
: ctx_(ctx), textures_{0}, num_verts2d_(0), num_surfs2d_(0) {}
|
||||
|
||||
GLBackend::~GLBackend() {
|
||||
DestroyFonts();
|
||||
DestroyVertexBuffers();
|
||||
DestroyShaders();
|
||||
DestroyTextures();
|
||||
DestroyFramebuffers();
|
||||
}
|
||||
|
||||
bool GLBackend::Init() {
|
||||
if (!ctx_.GLInit(&state_.video_width, &state_.video_height)) {
|
||||
if (!ctx_.GLInitContext(&state_.video_width, &state_.video_height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// can't initialize framebuffers here, must wait for first resize
|
||||
// InitFramebuffers();
|
||||
InitTextures();
|
||||
InitShaders();
|
||||
InitVertexBuffers();
|
||||
|
@ -79,6 +76,11 @@ bool GLBackend::Init() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void GLBackend::ResizeVideo(int width, int height) {
|
||||
state_.video_width = width;
|
||||
state_.video_height = height;
|
||||
}
|
||||
|
||||
TextureHandle GLBackend::RegisterTexture(PixelFormat format, FilterMode filter,
|
||||
bool gen_mipmaps, int width,
|
||||
int height, const uint8_t *buffer) {
|
||||
|
@ -141,114 +143,16 @@ void GLBackend::FreeTexture(TextureHandle handle) {
|
|||
gltex = 0;
|
||||
}
|
||||
|
||||
void GLBackend::SetFramebufferSize(Framebuffer fb, int width, int height) {
|
||||
switch (fb) {
|
||||
case FB_DEFAULT:
|
||||
state_.video_width = width;
|
||||
state_.video_height = height;
|
||||
break;
|
||||
|
||||
case FB_TILE_ACCELERATOR:
|
||||
state_.ta_width = width;
|
||||
state_.ta_height = height;
|
||||
DestroyFramebuffers();
|
||||
InitFramebuffers();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GLBackend::GetFramebufferSize(Framebuffer fb, int *width, int *height) {
|
||||
switch (fb) {
|
||||
case FB_DEFAULT:
|
||||
*width = state_.video_width;
|
||||
*height = state_.video_height;
|
||||
break;
|
||||
|
||||
case FB_TILE_ACCELERATOR:
|
||||
*width = state_.ta_width;
|
||||
*height = state_.ta_height;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GLBackend::BeginFrame() {
|
||||
BindFramebuffer(FB_DEFAULT);
|
||||
|
||||
Clear(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void GLBackend::BindFramebuffer(Framebuffer fb) {
|
||||
Flush2D();
|
||||
|
||||
GLuint buffer = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
switch (fb) {
|
||||
case FB_DEFAULT:
|
||||
buffer = 0;
|
||||
width = state_.video_width;
|
||||
height = state_.video_height;
|
||||
break;
|
||||
|
||||
case FB_TILE_ACCELERATOR:
|
||||
// ensure ta was initialized through SetFramebufferSize
|
||||
CHECK_GT(fb_ta_, 0);
|
||||
buffer = fb_ta_;
|
||||
width = state_.ta_width;
|
||||
height = state_.ta_height;
|
||||
break;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, buffer);
|
||||
glViewport(0, 0, width, height);
|
||||
glScissor(0, 0, width, height);
|
||||
}
|
||||
|
||||
void GLBackend::Clear(float r, float g, float b, float a) {
|
||||
SetDepthMask(true);
|
||||
|
||||
glClearColor(r, g, b, a);
|
||||
glViewport(0, 0, state_.video_width, state_.video_height);
|
||||
glScissor(0, 0, state_.video_width, state_.video_height);
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void GLBackend::RenderFramebuffer(Framebuffer fb) {
|
||||
switch (fb) {
|
||||
case FB_DEFAULT:
|
||||
LOG_FATAL("Unsupported");
|
||||
break;
|
||||
|
||||
case FB_TILE_ACCELERATOR:
|
||||
Vertex2D *vert = AllocVertices2D(
|
||||
{GL_TRIANGLES, (int)fb_ta_color_, BLEND_NONE, BLEND_NONE, 0}, 6);
|
||||
|
||||
Q0(vert, x, 0.0f);
|
||||
Q0(vert, y, 0.0f);
|
||||
Q0(vert, color, 0xffffffff);
|
||||
Q0(vert, u, 0.0f);
|
||||
Q0(vert, v, 1.0f);
|
||||
|
||||
Q1(vert, x, (float)state_.video_width);
|
||||
Q1(vert, y, 0.0f);
|
||||
Q1(vert, color, 0xffffffff);
|
||||
Q1(vert, u, 1.0f);
|
||||
Q1(vert, v, 1.0f);
|
||||
|
||||
Q2(vert, x, (float)state_.video_width);
|
||||
Q2(vert, y, (float)state_.video_height);
|
||||
Q2(vert, color, 0xffffffff);
|
||||
Q2(vert, u, 1.0f);
|
||||
Q2(vert, v, 0.0f);
|
||||
|
||||
Q3(vert, x, 0.0f);
|
||||
Q3(vert, y, (float)state_.video_height);
|
||||
Q3(vert, color, 0xffffffff);
|
||||
Q3(vert, u, 0.0f);
|
||||
Q3(vert, v, 0.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GLBackend::RenderText2D(int x, int y, float point_size, uint32_t color,
|
||||
const char *text) {
|
||||
float fx = (float)x;
|
||||
|
@ -429,43 +333,6 @@ void GLBackend::EndFrame() {
|
|||
ctx_.GLSwapBuffers();
|
||||
}
|
||||
|
||||
void GLBackend::InitFramebuffers() {
|
||||
CHECK_GT(state_.ta_width, 0);
|
||||
CHECK_GT(state_.ta_height, 0);
|
||||
|
||||
glGenFramebuffers(1, &fb_ta_);
|
||||
glGenTextures(1, &fb_ta_color_);
|
||||
glGenRenderbuffers(1, &fb_ta_depth_);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, fb_ta_color_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, state_.ta_width, state_.ta_height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, fb_ta_depth_);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, state_.ta_width,
|
||||
state_.ta_height);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb_ta_);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, fb_ta_color_, 0);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, fb_ta_depth_);
|
||||
GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
|
||||
CHECK_EQ(status, GL_FRAMEBUFFER_COMPLETE);
|
||||
Clear(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void GLBackend::DestroyFramebuffers() {
|
||||
glDeleteFramebuffers(1, &fb_ta_);
|
||||
glDeleteTextures(1, &fb_ta_color_);
|
||||
glDeleteRenderbuffers(1, &fb_ta_depth_);
|
||||
}
|
||||
|
||||
void GLBackend::InitTextures() {
|
||||
uint8_t pixels[64 * 64 * 4];
|
||||
memset(pixels, 0xff, sizeof(pixels));
|
||||
|
@ -580,12 +447,7 @@ void GLBackend::DestroyFonts() {
|
|||
}
|
||||
}
|
||||
|
||||
void GLBackend::SetupDefaultState() {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glDrawBuffer(GL_FRONT_AND_BACK);
|
||||
Clear(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glDrawBuffer(GL_BACK);
|
||||
}
|
||||
void GLBackend::SetupDefaultState() { glEnable(GL_SCISSOR_TEST); }
|
||||
|
||||
void GLBackend::SetDepthMask(bool enabled) {
|
||||
if (state_.depth_mask == enabled) {
|
||||
|
|
|
@ -71,18 +71,14 @@ class GLBackend : public Backend {
|
|||
|
||||
bool Init();
|
||||
|
||||
void ResizeVideo(int width, int height);
|
||||
|
||||
TextureHandle RegisterTexture(PixelFormat format, FilterMode filter,
|
||||
bool gen_mipmaps, int width, int height,
|
||||
const uint8_t *buffer);
|
||||
void FreeTexture(TextureHandle handle);
|
||||
|
||||
void SetFramebufferSize(Framebuffer fb, int width, int height);
|
||||
void GetFramebufferSize(Framebuffer fb, int *width, int *height);
|
||||
|
||||
void BeginFrame();
|
||||
void BindFramebuffer(Framebuffer fb);
|
||||
void Clear(float r, float g, float b, float a);
|
||||
void RenderFramebuffer(Framebuffer fb);
|
||||
void RenderText2D(int x, int y, float point_size, uint32_t color,
|
||||
const char *text);
|
||||
void RenderBox2D(int x0, int y0, int x1, int y1, uint32_t color,
|
||||
|
@ -95,8 +91,6 @@ class GLBackend : public Backend {
|
|||
void EndFrame();
|
||||
|
||||
private:
|
||||
void InitFramebuffers();
|
||||
void DestroyFramebuffers();
|
||||
void InitTextures();
|
||||
void DestroyTextures();
|
||||
void InitShaders();
|
||||
|
@ -130,8 +124,6 @@ class GLBackend : public Backend {
|
|||
GLuint ui_vao_, ui_vbo_;
|
||||
GLuint ta_vao_, ta_vbo_;
|
||||
|
||||
GLuint fb_ta_, fb_ta_color_, fb_ta_depth_;
|
||||
|
||||
Vertex2D verts2d_[MAX_2D_VERTICES];
|
||||
int num_verts2d_;
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@ namespace renderer {
|
|||
|
||||
class GLContext {
|
||||
public:
|
||||
virtual bool GLInit(int *width, int *height) = 0;
|
||||
virtual bool GLInitContext(int *width, int *height) = 0;
|
||||
virtual void GLDestroyContext() = 0;
|
||||
virtual void GLSwapBuffers() = 0;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "core/core.h"
|
||||
#include "system/system.h"
|
||||
|
||||
#define DEFAULT_VIDEO_WIDTH 800
|
||||
#define DEFAULT_VIDEO_HEIGHT 600
|
||||
#define DEFAULT_VIDEO_WIDTH 640
|
||||
#define DEFAULT_VIDEO_HEIGHT 480
|
||||
|
||||
using namespace dreavm::core;
|
||||
using namespace dreavm::system;
|
||||
|
@ -43,14 +43,10 @@ System::System()
|
|||
events_(MAX_EVENTS) {}
|
||||
|
||||
System::~System() {
|
||||
GLDestroyContext();
|
||||
DestroyInput();
|
||||
DestroyWindow();
|
||||
DestroySDL();
|
||||
|
||||
if (glcontext_) {
|
||||
SDL_GL_DeleteContext(glcontext_);
|
||||
glcontext_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool System::Init() {
|
||||
|
@ -69,16 +65,7 @@ bool System::Init() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void System::Tick() { PumpEvents(); }
|
||||
|
||||
void System::QueueEvent(const SystemEvent &ev) {
|
||||
if (events_.Full()) {
|
||||
LOG_WARNING("System event overflow");
|
||||
return;
|
||||
}
|
||||
|
||||
events_.PushBack(ev);
|
||||
}
|
||||
void System::PumpEvents() { PumpSDLEvents(); }
|
||||
|
||||
bool System::PollEvent(SystemEvent *ev) {
|
||||
if (events_.Empty()) {
|
||||
|
@ -91,11 +78,16 @@ bool System::PollEvent(SystemEvent *ev) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool System::GLInit(int *width, int *height) {
|
||||
bool System::GLInitContext(int *width, int *height) {
|
||||
// need at least a 3.3 core context for our shaders
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
// request a 24-bit depth buffer. 16-bits isn't enough precision when
|
||||
// unprojecting dreamcast coordinates, see TileRenderer::GetProjectionMatrix
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
|
||||
glcontext_ = SDL_GL_CreateContext(window_);
|
||||
if (!glcontext_) {
|
||||
LOG_WARNING("OpenGL context creation failed: %s");
|
||||
|
@ -119,10 +111,17 @@ bool System::GLInit(int *width, int *height) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void System::GLDestroyContext() {
|
||||
if (glcontext_) {
|
||||
SDL_GL_DeleteContext(glcontext_);
|
||||
glcontext_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void System::GLSwapBuffers() { SDL_GL_SwapWindow(window_); }
|
||||
|
||||
bool System::InitSDL() {
|
||||
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) < 0) {
|
||||
if (SDL_Init(0) < 0) {
|
||||
LOG_WARNING("SDL initialization failed: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
@ -133,11 +132,14 @@ bool System::InitSDL() {
|
|||
void System::DestroySDL() { SDL_Quit(); }
|
||||
|
||||
bool System::InitWindow() {
|
||||
unsigned window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
|
||||
LOG_WARNING("Video initialization failed: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
window_ = SDL_CreateWindow("dreavm", SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED, video_width_,
|
||||
video_height_, window_flags);
|
||||
video_height_, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
||||
if (!window_) {
|
||||
LOG_WARNING("Window creation failed: %s", SDL_GetError());
|
||||
return false;
|
||||
|
@ -147,8 +149,10 @@ bool System::InitWindow() {
|
|||
}
|
||||
|
||||
void System::DestroyWindow() {
|
||||
SDL_DestroyWindow(window_);
|
||||
window_ = nullptr;
|
||||
if (window_) {
|
||||
SDL_DestroyWindow(window_);
|
||||
window_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool System::InitInput() {
|
||||
|
@ -187,6 +191,15 @@ void System::DestroyJoystick() {
|
|||
}
|
||||
}
|
||||
|
||||
void System::QueueEvent(const SystemEvent &ev) {
|
||||
if (events_.Full()) {
|
||||
LOG_WARNING("System event overflow");
|
||||
return;
|
||||
}
|
||||
|
||||
events_.PushBack(ev);
|
||||
}
|
||||
|
||||
Keycode System::TranslateSDLKey(SDL_Keysym keysym) {
|
||||
Keycode out = K_UNKNOWN;
|
||||
|
||||
|
@ -818,5 +831,3 @@ void System::PumpSDLEvents() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void System::PumpEvents() { PumpSDLEvents(); }
|
||||
|
|
|
@ -14,7 +14,7 @@ enum {
|
|||
NUM_JOYSTICK_AXES = (K_AXIS15 - K_AXIS0) + 1,
|
||||
NUM_JOYSTICK_KEYS = (K_JOY31 - K_JOY0) + 1
|
||||
};
|
||||
enum SystemEventType { SE_NONE, SE_KEY, SE_MOUSEMOVE, SE_RESIZE };
|
||||
enum SystemEventType { SE_KEY, SE_MOUSEMOVE, SE_RESIZE };
|
||||
|
||||
struct SystemEvent {
|
||||
SystemEventType type;
|
||||
|
@ -41,10 +41,12 @@ class System : public renderer::GLContext {
|
|||
~System();
|
||||
|
||||
bool Init();
|
||||
void Tick();
|
||||
|
||||
void PumpEvents();
|
||||
bool PollEvent(SystemEvent *ev);
|
||||
|
||||
bool GLInit(int *width, int *height);
|
||||
bool GLInitContext(int *width, int *height);
|
||||
void GLDestroyContext();
|
||||
void GLSwapBuffers();
|
||||
|
||||
private:
|
||||
|
@ -61,7 +63,6 @@ class System : public renderer::GLContext {
|
|||
|
||||
Keycode TranslateSDLKey(SDL_Keysym keysym);
|
||||
void PumpSDLEvents();
|
||||
void PumpEvents();
|
||||
|
||||
int video_width_;
|
||||
int video_height_;
|
||||
|
|
|
@ -78,10 +78,6 @@ bool TraceReader::PatchPointers() {
|
|||
|
||||
// patch relative data pointers
|
||||
switch (curr_cmd->type) {
|
||||
case TRACE_RESIZE_VIDEO: {
|
||||
ptr += sizeof(*curr_cmd);
|
||||
} break;
|
||||
|
||||
case TRACE_INSERT_TEXTURE: {
|
||||
curr_cmd->insert_texture.texture += reinterpret_cast<intptr_t>(ptr);
|
||||
curr_cmd->insert_texture.palette += reinterpret_cast<intptr_t>(ptr);
|
||||
|
@ -111,18 +107,10 @@ bool TraceReader::PatchPointers() {
|
|||
bool TraceReader::PatchOverrides() {
|
||||
TraceCommand *cmd = cmd_head();
|
||||
|
||||
TraceCommand *last_resize = nullptr;
|
||||
std::unordered_map<uint32_t, TraceCommand *> last_inserts;
|
||||
|
||||
while (cmd) {
|
||||
switch (cmd->type) {
|
||||
case TRACE_RESIZE_VIDEO: {
|
||||
if (last_resize) {
|
||||
cmd->override = last_resize;
|
||||
}
|
||||
last_resize = cmd;
|
||||
} break;
|
||||
|
||||
case TRACE_INSERT_TEXTURE: {
|
||||
uint32_t texture_key = TextureCache::GetTextureKey(
|
||||
cmd->insert_texture.tsp, cmd->insert_texture.tcw);
|
||||
|
@ -168,15 +156,6 @@ void TraceWriter::Close() {
|
|||
}
|
||||
}
|
||||
|
||||
void TraceWriter::WriteResizeVideo(int width, int height) {
|
||||
TraceCommand cmd;
|
||||
cmd.type = TRACE_RESIZE_VIDEO;
|
||||
cmd.resize_video.width = width;
|
||||
cmd.resize_video.height = height;
|
||||
|
||||
CHECK_EQ(fwrite(&cmd, sizeof(cmd), 1, file_), 1);
|
||||
}
|
||||
|
||||
void TraceWriter::WriteInsertTexture(const TSP &tsp, const TCW &tcw,
|
||||
const uint8_t *texture, int texture_size,
|
||||
const uint8_t *palette, int palette_size) {
|
||||
|
@ -205,6 +184,8 @@ void TraceWriter::WriteRenderContext(TileContext *tactx) {
|
|||
cmd.render_context.autosort = tactx->autosort;
|
||||
cmd.render_context.stride = tactx->stride;
|
||||
cmd.render_context.pal_pxl_format = tactx->pal_pxl_format;
|
||||
cmd.render_context.video_width = tactx->video_width;
|
||||
cmd.render_context.video_height = tactx->video_height;
|
||||
cmd.render_context.bg_isp = tactx->bg_isp;
|
||||
cmd.render_context.bg_tsp = tactx->bg_tsp;
|
||||
cmd.render_context.bg_tcw = tactx->bg_tcw;
|
||||
|
|
|
@ -6,11 +6,7 @@
|
|||
namespace dreavm {
|
||||
namespace trace {
|
||||
|
||||
enum TraceCommandType {
|
||||
TRACE_RESIZE_VIDEO,
|
||||
TRACE_INSERT_TEXTURE,
|
||||
TRACE_RENDER_CONTEXT
|
||||
};
|
||||
enum TraceCommandType { TRACE_INSERT_TEXTURE, TRACE_RENDER_CONTEXT };
|
||||
|
||||
struct TraceCommand {
|
||||
TraceCommand() : prev(nullptr), next(nullptr), override(nullptr) {}
|
||||
|
@ -25,11 +21,6 @@ struct TraceCommand {
|
|||
// the data pointers in these structs are written out relative to the cmd,
|
||||
// and patched to absolute pointers on read
|
||||
union {
|
||||
struct {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
} resize_video;
|
||||
|
||||
struct {
|
||||
holly::TSP tsp;
|
||||
holly::TCW tcw;
|
||||
|
@ -45,6 +36,8 @@ struct TraceCommand {
|
|||
int8_t autosort;
|
||||
uint32_t stride;
|
||||
uint32_t pal_pxl_format;
|
||||
uint32_t video_width;
|
||||
uint32_t video_height;
|
||||
holly::ISP_TSP bg_isp;
|
||||
holly::TSP bg_tsp;
|
||||
holly::TCW bg_tcw;
|
||||
|
@ -85,7 +78,6 @@ class TraceWriter {
|
|||
bool Open(const char *filename);
|
||||
void Close();
|
||||
|
||||
void WriteResizeVideo(int width, int height);
|
||||
void WriteInsertTexture(const holly::TSP &tsp, const holly::TCW &tcw,
|
||||
const uint8_t *texture, int texture_size,
|
||||
const uint8_t *palette, int palette_size);
|
||||
|
|
|
@ -42,13 +42,34 @@ TextureHandle TraceTextureCache::GetTexture(
|
|||
return texture.handle;
|
||||
}
|
||||
|
||||
TraceViewer::TraceViewer(System &sys) : sys_(sys), renderer_(texcache_) {
|
||||
rb_ = new GLBackend(sys);
|
||||
TraceViewer::TraceViewer() : tile_renderer_(texcache_) {
|
||||
rb_ = new GLBackend(sys_);
|
||||
}
|
||||
|
||||
TraceViewer::~TraceViewer() { delete rb_; }
|
||||
|
||||
void TraceViewer::Run(const char *path) {
|
||||
if (!Init()) {
|
||||
LOG_WARNING("Failed to initialize trace viewer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Parse(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
PumpEvents();
|
||||
|
||||
RenderFrame();
|
||||
}
|
||||
}
|
||||
|
||||
bool TraceViewer::Init() {
|
||||
if (!sys_.Init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rb_->Init()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -56,14 +77,15 @@ bool TraceViewer::Init() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool TraceViewer::Load(const char *path) {
|
||||
bool TraceViewer::Parse(const char *path) {
|
||||
if (!reader_.Parse(path)) {
|
||||
LOG_INFO("Failed to parse %s", path);
|
||||
LOG_WARNING("Failed to parse %s", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
num_frames_ = GetNumFrames();
|
||||
if (!num_frames_) {
|
||||
LOG_WARNING("No frames in %s", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -74,24 +96,23 @@ bool TraceViewer::Load(const char *path) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void TraceViewer::Tick() {
|
||||
PumpEvents();
|
||||
|
||||
RenderFrame();
|
||||
}
|
||||
|
||||
void TraceViewer::PumpEvents() {
|
||||
SystemEvent ev;
|
||||
|
||||
sys_.PumpEvents();
|
||||
|
||||
while (sys_.PollEvent(&ev)) {
|
||||
if (ev.type == SE_KEY) {
|
||||
if (ev.key.code == K_LEFT && ev.key.value) {
|
||||
PrevContext();
|
||||
} else if (ev.key.code == K_RIGHT && ev.key.value) {
|
||||
NextContext();
|
||||
}
|
||||
} else if (ev.type == SE_RESIZE) {
|
||||
rb_->SetFramebufferSize(FB_DEFAULT, ev.resize.width, ev.resize.height);
|
||||
switch (ev.type) {
|
||||
case SE_KEY: {
|
||||
if (ev.key.code == K_LEFT && ev.key.value) {
|
||||
PrevContext();
|
||||
} else if (ev.key.code == K_RIGHT && ev.key.value) {
|
||||
NextContext();
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +120,7 @@ void TraceViewer::PumpEvents() {
|
|||
void TraceViewer::RenderFrame() {
|
||||
rb_->BeginFrame();
|
||||
|
||||
rb_->RenderFramebuffer(FB_TILE_ACCELERATOR);
|
||||
tile_renderer_.RenderContext(¤t_ctx_, rb_);
|
||||
|
||||
// render stats
|
||||
char stats[512];
|
||||
|
@ -136,6 +157,8 @@ void TraceViewer::CopyCommandToContext(const TraceCommand *cmd,
|
|||
ctx->bg_tsp = cmd->render_context.bg_tsp;
|
||||
ctx->bg_tcw = cmd->render_context.bg_tcw;
|
||||
ctx->bg_depth = cmd->render_context.bg_depth;
|
||||
ctx->video_width = cmd->render_context.video_width;
|
||||
ctx->video_height = cmd->render_context.video_height;
|
||||
memcpy(ctx->bg_vertices, cmd->render_context.bg_vertices,
|
||||
cmd->render_context.bg_vertices_size);
|
||||
memcpy(ctx->data, cmd->render_context.data, cmd->render_context.data_size);
|
||||
|
@ -155,14 +178,7 @@ void TraceViewer::PrevContext() {
|
|||
while (current_cmd_) {
|
||||
TraceCommand *override = current_cmd_->override;
|
||||
|
||||
if (current_cmd_->type == TRACE_RESIZE_VIDEO) {
|
||||
if (override) {
|
||||
CHECK_EQ(override->type, TRACE_RESIZE_VIDEO);
|
||||
rb_->SetFramebufferSize(FB_TILE_ACCELERATOR,
|
||||
override->resize_video.width,
|
||||
override->resize_video.height);
|
||||
}
|
||||
} else if (current_cmd_->type == TRACE_INSERT_TEXTURE) {
|
||||
if (current_cmd_->type == TRACE_INSERT_TEXTURE) {
|
||||
texcache_.RemoveTexture(current_cmd_->insert_texture.tsp,
|
||||
current_cmd_->insert_texture.tcw);
|
||||
|
||||
|
@ -183,10 +199,7 @@ void TraceViewer::PrevContext() {
|
|||
|
||||
CHECK_NOTNULL(current_cmd_);
|
||||
|
||||
// render the context
|
||||
TileContext ctx;
|
||||
CopyCommandToContext(current_cmd_, &ctx);
|
||||
renderer_.RenderContext(&ctx, rb_);
|
||||
CopyCommandToContext(current_cmd_, ¤t_ctx_);
|
||||
}
|
||||
|
||||
void TraceViewer::NextContext() {
|
||||
|
@ -198,11 +211,7 @@ void TraceViewer::NextContext() {
|
|||
current_cmd_ = current_cmd_ ? current_cmd_->next : reader_.cmd_head();
|
||||
|
||||
while (current_cmd_) {
|
||||
if (current_cmd_->type == TRACE_RESIZE_VIDEO) {
|
||||
rb_->SetFramebufferSize(FB_TILE_ACCELERATOR,
|
||||
current_cmd_->resize_video.width,
|
||||
current_cmd_->resize_video.height);
|
||||
} else if (current_cmd_->type == TRACE_INSERT_TEXTURE) {
|
||||
if (current_cmd_->type == TRACE_INSERT_TEXTURE) {
|
||||
texcache_.AddTexture(current_cmd_->insert_texture.tsp,
|
||||
current_cmd_->insert_texture.tcw,
|
||||
current_cmd_->insert_texture.texture,
|
||||
|
@ -219,7 +228,5 @@ void TraceViewer::NextContext() {
|
|||
CHECK_NOTNULL(current_cmd_);
|
||||
|
||||
// render the context
|
||||
TileContext ctx;
|
||||
CopyCommandToContext(current_cmd_, &ctx);
|
||||
renderer_.RenderContext(&ctx, rb_);
|
||||
CopyCommandToContext(current_cmd_, ¤t_ctx_);
|
||||
}
|
||||
|
|
|
@ -34,14 +34,15 @@ class TraceTextureCache : public holly::TextureCache {
|
|||
|
||||
class TraceViewer {
|
||||
public:
|
||||
TraceViewer(system::System &sys);
|
||||
TraceViewer();
|
||||
~TraceViewer();
|
||||
|
||||
bool Init();
|
||||
bool Load(const char *path);
|
||||
void Tick();
|
||||
void Run(const char *path);
|
||||
|
||||
private:
|
||||
bool Init();
|
||||
bool Parse(const char *path);
|
||||
|
||||
void PumpEvents();
|
||||
void RenderFrame();
|
||||
|
||||
|
@ -50,15 +51,16 @@ class TraceViewer {
|
|||
void PrevContext();
|
||||
void NextContext();
|
||||
|
||||
system::System &sys_;
|
||||
system::System sys_;
|
||||
TraceTextureCache texcache_;
|
||||
holly::TileRenderer renderer_;
|
||||
holly::TileRenderer tile_renderer_;
|
||||
renderer::Backend *rb_;
|
||||
|
||||
TraceReader reader_;
|
||||
TraceCommand *current_cmd_;
|
||||
int num_frames_;
|
||||
int current_frame_;
|
||||
holly::TileContext current_ctx_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue