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:
Anthony Pesch 2015-09-01 22:44:53 -07:00
parent 1eaac911d8
commit 0a1dbf1b66
21 changed files with 286 additions and 503 deletions

View File

@ -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

View File

@ -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_;

View File

@ -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;
}

View File

@ -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_;
};
}
}

View File

@ -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

View File

@ -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_;

View File

@ -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) {

View File

@ -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_;
};

View File

@ -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;

View File

@ -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];

View File

@ -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();

View File

@ -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,

View File

@ -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) {

View File

@ -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_;

View File

@ -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;
};
}

View File

@ -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(); }

View File

@ -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_;

View File

@ -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;

View File

@ -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);

View File

@ -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(&current_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_, &current_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_, &current_ctx_);
}

View File

@ -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_;
};
}
}