mirror of https://github.com/inolen/redream.git
233 lines
5.9 KiB
C++
233 lines
5.9 KiB
C++
#include <algorithm>
|
|
#include "core/core.h"
|
|
#include "hw/holly/tile_accelerator.h"
|
|
#include "renderer/gl_backend.h"
|
|
#include "trace/trace.h"
|
|
#include "trace/trace_viewer.h"
|
|
|
|
using namespace dreavm::core;
|
|
using namespace dreavm::hw::holly;
|
|
using namespace dreavm::renderer;
|
|
using namespace dreavm::system;
|
|
using namespace dreavm::trace;
|
|
|
|
void TraceTextureCache::AddTexture(const TSP &tsp, TCW &tcw,
|
|
const uint8_t *palette,
|
|
const uint8_t *texture) {
|
|
TextureKey texture_key = TextureCache::GetTextureKey(tsp, tcw);
|
|
textures_[texture_key] =
|
|
TextureInst{tsp, tcw, palette, texture, (TextureHandle)0};
|
|
}
|
|
|
|
void TraceTextureCache::RemoveTexture(const TSP &tsp, TCW &tcw) {
|
|
TextureKey texture_key = TextureCache::GetTextureKey(tsp, tcw);
|
|
textures_.erase(texture_key);
|
|
}
|
|
|
|
TextureHandle TraceTextureCache::GetTexture(
|
|
const TSP &tsp, const TCW &tcw, RegisterTextureCallback register_cb) {
|
|
TextureKey texture_key = TextureCache::GetTextureKey(tsp, tcw);
|
|
|
|
auto it = textures_.find(texture_key);
|
|
CHECK_NE(it, textures_.end(), "Texture wasn't available in cache");
|
|
|
|
TextureInst &texture = it->second;
|
|
|
|
// register the texture if it hasn't already been
|
|
if (!texture.handle) {
|
|
// TODO compare tex_it->tsp and tex_it->tcw with incoming?
|
|
texture.handle = register_cb(texture.palette, texture.texture);
|
|
}
|
|
|
|
return texture.handle;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TraceViewer::Parse(const char *path) {
|
|
if (!reader_.Parse(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;
|
|
}
|
|
|
|
current_frame_ = 0;
|
|
current_cmd_ = nullptr;
|
|
NextContext();
|
|
|
|
return true;
|
|
}
|
|
|
|
void TraceViewer::PumpEvents() {
|
|
SystemEvent ev;
|
|
|
|
sys_.PumpEvents();
|
|
|
|
while (sys_.PollEvent(&ev)) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TraceViewer::RenderFrame() {
|
|
rb_->BeginFrame();
|
|
|
|
tile_renderer_.RenderContext(¤t_ctx_, rb_);
|
|
|
|
// render stats
|
|
char stats[512];
|
|
snprintf(stats, sizeof(stats), "frame %d / %d", current_frame_, num_frames_);
|
|
rb_->RenderText2D(0, 0, 12.0f, 0xffffffff, stats);
|
|
|
|
rb_->EndFrame();
|
|
}
|
|
|
|
int TraceViewer::GetNumFrames() {
|
|
int num_frames = 0;
|
|
|
|
TraceCommand *cmd = reader_.cmd_head();
|
|
|
|
while (cmd) {
|
|
if (cmd->type == TRACE_RENDER_CONTEXT) {
|
|
num_frames++;
|
|
}
|
|
|
|
cmd = cmd->next;
|
|
}
|
|
|
|
return num_frames;
|
|
}
|
|
|
|
void TraceViewer::CopyCommandToContext(const TraceCommand *cmd,
|
|
TileContext *ctx) {
|
|
CHECK_EQ(cmd->type, TRACE_RENDER_CONTEXT);
|
|
|
|
ctx->autosort = cmd->render_context.autosort;
|
|
ctx->stride = cmd->render_context.stride;
|
|
ctx->pal_pxl_format = cmd->render_context.pal_pxl_format;
|
|
ctx->bg_isp = cmd->render_context.bg_isp;
|
|
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);
|
|
ctx->size = cmd->render_context.data_size;
|
|
}
|
|
|
|
void TraceViewer::PrevContext() {
|
|
int prev_frame = std::max(1, current_frame_ - 1);
|
|
if (prev_frame == current_frame_) {
|
|
return;
|
|
}
|
|
|
|
current_cmd_ = current_cmd_->prev;
|
|
|
|
// scrub through commands until the previous context is reached. for each
|
|
// command we move backwards through, re-apply the value it overrode
|
|
while (current_cmd_) {
|
|
TraceCommand *override = current_cmd_->override;
|
|
|
|
if (current_cmd_->type == TRACE_INSERT_TEXTURE) {
|
|
texcache_.RemoveTexture(current_cmd_->insert_texture.tsp,
|
|
current_cmd_->insert_texture.tcw);
|
|
|
|
if (override) {
|
|
CHECK_EQ(override->type, TRACE_INSERT_TEXTURE);
|
|
texcache_.AddTexture(
|
|
override->insert_texture.tsp, override->insert_texture.tcw,
|
|
override->insert_texture.palette, override->insert_texture.texture);
|
|
}
|
|
} else if (current_cmd_->type == TRACE_RENDER_CONTEXT) {
|
|
if (--current_frame_ == prev_frame) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
current_cmd_ = current_cmd_->prev;
|
|
}
|
|
|
|
CHECK_NOTNULL(current_cmd_);
|
|
|
|
CopyCommandToContext(current_cmd_, ¤t_ctx_);
|
|
}
|
|
|
|
void TraceViewer::NextContext() {
|
|
int next_frame = std::min(num_frames_, current_frame_ + 1);
|
|
if (next_frame == current_frame_) {
|
|
return;
|
|
}
|
|
|
|
current_cmd_ = current_cmd_ ? current_cmd_->next : reader_.cmd_head();
|
|
|
|
while (current_cmd_) {
|
|
if (current_cmd_->type == TRACE_INSERT_TEXTURE) {
|
|
texcache_.AddTexture(current_cmd_->insert_texture.tsp,
|
|
current_cmd_->insert_texture.tcw,
|
|
current_cmd_->insert_texture.palette,
|
|
current_cmd_->insert_texture.texture);
|
|
} else if (current_cmd_->type == TRACE_RENDER_CONTEXT) {
|
|
if (++current_frame_ == next_frame) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
current_cmd_ = current_cmd_->next;
|
|
}
|
|
|
|
CHECK_NOTNULL(current_cmd_);
|
|
|
|
// render the context
|
|
CopyCommandToContext(current_cmd_, ¤t_ctx_);
|
|
}
|