add vis data to tie/tfrag and better framelimiting/lag (#1100)

* add vis data to tie/tfrag and better fps stuff

* better default
This commit is contained in:
water111
2022-01-20 00:22:03 -05:00
committed by GitHub
parent 751fd06513
commit 4648f78733
21 changed files with 285 additions and 59 deletions
+6 -5
View File
@@ -11,7 +11,7 @@
namespace tfrag3 {
constexpr int TFRAG3_VERSION = 8;
constexpr int TFRAG3_VERSION = 9;
// These vertices should be uploaded to the GPU at load time and don't change
struct PreloadedVertex {
@@ -42,8 +42,8 @@ struct StripDraw {
// to do culling, the above vertex stream is grouped.
// by following the visgroups and checking the visibility, you can leave out invisible vertices.
struct VisGroup {
u32 num = 0; // number of vertex indices in this group
u32 vis_idx = 0; // the visibility group they belong to
u32 num = 0; // number of vertex indices in this group
u32 vis_idx_in_pc_bvh = 0; // the visibility group they belong to (in BVH)
};
std::vector<VisGroup> vis_groups;
@@ -77,8 +77,9 @@ struct InstancedStripDraw {
struct VisNode {
math::Vector<float, 4> bsphere; // the bounding sphere, in meters (4096 = 1 game meter). w = rad
u16 child_id = 0xffff; // the ID of our first child.
u8 num_kids = 0xff; // number of children. The children are consecutive in memory
u8 flags = 0; // flags. If 1, we have a DrawVisNode child, otherwise a leaf.
u16 my_id = 0xffff;
u8 num_kids = 0xff; // number of children. The children are consecutive in memory
u8 flags = 0; // flags. If 1, we have a DrawVisNode child, otherwise a leaf.
};
// The leaf nodes don't actually exist in the vector of VisNodes, but instead they are ID's used
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include "common/util/Timer.h"
class FrameLimiter {
public:
void run(double target_fps, bool experimental_accurate_lag, double engine_time) {
double target_seconds;
if (experimental_accurate_lag) {
target_seconds = round_to_nearest_60fps(engine_time);
} else {
target_seconds = 1.f / target_fps;
}
double remaining_time = target_seconds - m_timer.getSeconds();
while (remaining_time > 0) {
if (remaining_time > 0.003) {
std::this_thread::sleep_for(std::chrono::microseconds(int(remaining_time * 1e6 * 0.5)));
}
remaining_time = target_seconds - m_timer.getSeconds();
}
m_timer.start();
}
private:
double round_to_nearest_60fps(double current) {
double one_frame = 1.f / 60.f;
int frames_missed = (current / one_frame); // rounds down
if (frames_missed > 4) {
frames_missed = 4;
}
return (frames_missed + 1) * one_frame;
}
Timer m_timer;
};
+7 -6
View File
@@ -117,6 +117,7 @@ VisNodeTree extract_vis_data(const level_tools::DrawableTreeTfrag* tree, u16 fir
}
vis.num_kids = elt.child_count;
vis.flags = elt.flags;
vis.my_id = elt.id;
assert(vis.flags == expecting_leaves ? 0 : 1);
assert(vis.num_kids > 0);
assert(vis.num_kids <= 8);
@@ -2036,8 +2037,8 @@ void make_tfrag3_data(std::map<u32, std::vector<GroupedDraw>>& draws,
for (auto& strip : draw.strips) {
tfrag3::StripDraw::VisGroup vgroup;
vgroup.vis_idx = strip.tfrag_id; // associate with the tfrag for culling
vgroup.num = strip.verts.size() + 1; // one for the primitive restart!
vgroup.vis_idx_in_pc_bvh = strip.tfrag_id; // associate with the tfrag for culling
vgroup.num = strip.verts.size() + 1; // one for the primitive restart!
tdraw.num_triangles += strip.verts.size() - 2;
for (auto& vert : strip.verts) {
@@ -2118,7 +2119,7 @@ void merge_groups(std::vector<tfrag3::StripDraw::VisGroup>& grps) {
std::vector<tfrag3::StripDraw::VisGroup> result;
result.push_back(grps.at(0));
for (size_t i = 1; i < grps.size(); i++) {
if (grps[i].vis_idx == result.back().vis_idx) {
if (grps[i].vis_idx_in_pc_bvh == result.back().vis_idx_in_pc_bvh) {
result.back().num += grps[i].num;
} else {
result.push_back(grps[i]);
@@ -2196,11 +2197,11 @@ void extract_tfrag(const level_tools::DrawableTreeTfrag* tree,
for (auto& draw : this_tree.draws) {
for (auto& str : draw.vis_groups) {
auto it = tfrag_parents.find(str.vis_idx);
auto it = tfrag_parents.find(str.vis_idx_in_pc_bvh);
if (it == tfrag_parents.end()) {
str.vis_idx = UINT32_MAX;
str.vis_idx_in_pc_bvh = UINT32_MAX;
} else {
str.vis_idx = it->second;
str.vis_idx_in_pc_bvh = it->second;
}
}
merge_groups(draw.vis_groups);
@@ -20,15 +20,6 @@ struct VisNodeTree {
bool only_children = false;
};
// The final result
struct ExtractedTFragmentTree {
// TFragmentKind kind = TFragmentKind::INVALID;
VisNodeTree vis_nodes;
u16 num_tfrags = 0;
u16 tfrag_base_idx = 0;
};
// will pool textures with others already in out.
void extract_tfrag(const level_tools::DrawableTreeTfrag* tree,
const std::string& debug_name,
+7 -6
View File
@@ -119,6 +119,7 @@ void extract_vis_data(const level_tools::DrawableTreeInstanceTie* tree,
}
vis.num_kids = elt.child_count;
vis.flags = elt.flags;
vis.my_id = elt.id;
assert(vis.flags == expecting_leaves ? 0 : 1);
assert(vis.num_kids > 0);
assert(vis.num_kids <= 8);
@@ -2182,8 +2183,8 @@ void add_vertices_and_static_draw(tfrag3::TieTree& tree,
// now we have a draw, time to add vertices
tfrag3::StripDraw::VisGroup vgroup;
vgroup.vis_idx = inst.vis_id; // associate with the instance for culling
vgroup.num = strip.verts.size() + 1; // one for the primitive restart!
vgroup.vis_idx_in_pc_bvh = inst.vis_id; // associate with the instance for culling
vgroup.num = strip.verts.size() + 1; // one for the primitive restart!
draw_to_add_to->num_triangles += strip.verts.size() - 2;
for (auto& vert : strip.verts) {
tfrag3::PreloadedVertex vtx;
@@ -2247,7 +2248,7 @@ void merge_groups(std::vector<tfrag3::StripDraw::VisGroup>& grps) {
std::vector<tfrag3::StripDraw::VisGroup> result;
result.push_back(grps.at(0));
for (size_t i = 1; i < grps.size(); i++) {
if (grps[i].vis_idx == result.back().vis_idx) {
if (grps[i].vis_idx_in_pc_bvh == result.back().vis_idx_in_pc_bvh) {
result.back().num += grps[i].num;
} else {
result.push_back(grps[i]);
@@ -2329,11 +2330,11 @@ void extract_tie(const level_tools::DrawableTreeInstanceTie* tree,
// remap vis indices and merge
for (auto& draw : this_tree.static_draws) {
for (auto& str : draw.vis_groups) {
auto it = instance_parents.find(str.vis_idx);
auto it = instance_parents.find(str.vis_idx_in_pc_bvh);
if (it == instance_parents.end()) {
str.vis_idx = UINT32_MAX;
str.vis_idx_in_pc_bvh = UINT32_MAX;
} else {
str.vis_idx = it->second;
str.vis_idx_in_pc_bvh = it->second;
}
}
merge_groups(draw.vis_groups);
@@ -55,4 +55,11 @@ void SkipRenderer::render(DmaFollower& dma,
while (dma.current_tag_offset() != render_state->next_bucket) {
dma.read_and_advance();
}
}
void SharedRenderState::reset() {
has_camera_planes = false;
for (auto& x : occlusion_vis) {
x.valid = false;
}
}
@@ -43,6 +43,11 @@ enum class BucketId {
MAX_BUCKETS = 69
};
struct LevelVis {
bool valid = false;
u8 data[2048];
};
/*!
* The main renderer will contain a single SharedRenderState that's passed to all bucket renderers.
* This allows bucket renders to share textures and shaders.
@@ -63,8 +68,11 @@ struct SharedRenderState {
bool dump_playback = false;
bool use_sky_cpu = true;
bool use_occlusion_culling = true;
void reset();
bool has_camera_planes = false;
LevelVis occlusion_vis[2];
math::Vector4f camera_planes[4];
};
@@ -72,11 +72,11 @@ void OpenGLRenderer::init_bucket_renderers() {
init_bucket_renderer<SkyRenderer>("sky", BucketId::SKY_DRAW);
init_bucket_renderer<TextureUploadHandler>("tfrag-tex-0", BucketId::TFRAG_TEX_LEVEL0);
init_bucket_renderer<TFragment>("tfrag-0", BucketId::TFRAG_LEVEL0, normal_tfrags, false);
init_bucket_renderer<Tie3>("tie-0", BucketId::TIE_LEVEL0);
init_bucket_renderer<TFragment>("tfrag-0", BucketId::TFRAG_LEVEL0, normal_tfrags, false, 0);
init_bucket_renderer<Tie3>("tie-0", BucketId::TIE_LEVEL0, 0);
init_bucket_renderer<TextureUploadHandler>("tfrag-tex-1", BucketId::TFRAG_TEX_LEVEL1);
init_bucket_renderer<TFragment>("tfrag-1", BucketId::TFRAG_LEVEL1, normal_tfrags, false);
init_bucket_renderer<Tie3>("tie-1", BucketId::TIE_LEVEL1);
init_bucket_renderer<TFragment>("tfrag-1", BucketId::TFRAG_LEVEL1, normal_tfrags, false, 1);
init_bucket_renderer<Tie3>("tie-1", BucketId::TIE_LEVEL1, 1);
init_bucket_renderer<TextureUploadHandler>("shrub-tex-0", BucketId::SHRUB_TEX_LEVEL0);
init_bucket_renderer<TextureUploadHandler>("shrub-tex-1", BucketId::SHRUB_TEX_LEVEL1);
init_bucket_renderer<TextureUploadHandler>("alpha-tex-0", BucketId::ALPHA_TEX_LEVEL0);
@@ -84,15 +84,17 @@ void OpenGLRenderer::init_bucket_renderers() {
auto sky_gpu_blender = std::make_shared<SkyBlendGPU>();
auto sky_cpu_blender = std::make_shared<SkyBlendCPU>();
init_bucket_renderer<SkyBlendHandler>("sky-blend-and-tfrag-trans-0",
BucketId::TFRAG_TRANS0_AND_SKY_BLEND_LEVEL0,
BucketId::TFRAG_TRANS0_AND_SKY_BLEND_LEVEL0, 0,
sky_gpu_blender, sky_cpu_blender);
init_bucket_renderer<TFragment>("tfrag-dirt-0", BucketId::TFRAG_DIRT_LEVEL0, dirt_tfrags, false);
init_bucket_renderer<TFragment>("tfrag-ice-0", BucketId::TFRAG_ICE_LEVEL0, ice_tfrags, false);
init_bucket_renderer<TFragment>("tfrag-dirt-0", BucketId::TFRAG_DIRT_LEVEL0, dirt_tfrags, false,
0);
init_bucket_renderer<TFragment>("tfrag-ice-0", BucketId::TFRAG_ICE_LEVEL0, ice_tfrags, false, 0);
init_bucket_renderer<SkyBlendHandler>("sky-blend-and-tfrag-trans-1",
BucketId::TFRAG_TRANS1_AND_SKY_BLEND_LEVEL1,
BucketId::TFRAG_TRANS1_AND_SKY_BLEND_LEVEL1, 1,
sky_gpu_blender, sky_cpu_blender);
init_bucket_renderer<TFragment>("tfrag-dirt-1", BucketId::TFRAG_DIRT_LEVEL1, dirt_tfrags, false);
init_bucket_renderer<TFragment>("tfrag-ice-1", BucketId::TFRAG_ICE_LEVEL1, ice_tfrags, false);
init_bucket_renderer<TFragment>("tfrag-dirt-1", BucketId::TFRAG_DIRT_LEVEL1, dirt_tfrags, false,
1);
init_bucket_renderer<TFragment>("tfrag-ice-1", BucketId::TFRAG_ICE_LEVEL1, ice_tfrags, false, 1);
init_bucket_renderer<TextureUploadHandler>("pris-tex-0", BucketId::PRIS_TEX_LEVEL0);
init_bucket_renderer<TextureUploadHandler>("pris-tex-1", BucketId::PRIS_TEX_LEVEL1);
init_bucket_renderer<TextureUploadHandler>("water-tex-0", BucketId::WATER_TEX_LEVEL0);
@@ -117,6 +119,7 @@ void OpenGLRenderer::init_bucket_renderers() {
*/
void OpenGLRenderer::render(DmaFollower dma, const RenderOptions& settings) {
m_profiler.clear();
m_render_state.reset();
m_render_state.dump_playback = settings.playing_from_dump;
m_render_state.ee_main_memory = settings.playing_from_dump ? nullptr : g_ee_main_mem;
m_render_state.offset_of_s7 = offset_of_s7();
@@ -173,6 +176,7 @@ void OpenGLRenderer::draw_renderer_selection_window() {
ImGui::Begin("Renderer Debug");
ImGui::Checkbox("Sky CPU", &m_render_state.use_sky_cpu);
ImGui::Checkbox("Occlusion Cull", &m_render_state.use_occlusion_culling);
for (size_t i = 0; i < m_bucket_renderers.size(); i++) {
auto renderer = m_bucket_renderers[i].get();
@@ -21,6 +21,7 @@
SkyBlendHandler::SkyBlendHandler(const std::string& name,
BucketId my_id,
int level_id,
std::shared_ptr<SkyBlendGPU> shared_blender,
std::shared_ptr<SkyBlendCPU> shared_blender_cpu)
: BucketRenderer(name, my_id),
@@ -29,7 +30,8 @@ SkyBlendHandler::SkyBlendHandler(const std::string& name,
m_tfrag_renderer(fmt::format("tfrag-{}", name),
my_id,
{tfrag3::TFragmentTreeKind::TRANS, tfrag3::TFragmentTreeKind::LOWRES_TRANS},
true) {}
true,
level_id) {}
void SkyBlendHandler::handle_sky_copies(DmaFollower& dma,
SharedRenderState* render_state,
@@ -14,6 +14,7 @@ class SkyBlendHandler : public BucketRenderer {
public:
SkyBlendHandler(const std::string& name,
BucketId my_id,
int level_id,
std::shared_ptr<SkyBlendGPU> shared_gpu_blender,
std::shared_ptr<SkyBlendCPU> shared_cpu_blender);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
+21 -2
View File
@@ -4,14 +4,17 @@
#include "third-party/imgui/imgui.h"
void FrameTimeRecorder::finish_frame() {
m_frame_times[m_idx++] = m_timer.getMs();
m_frame_times[m_idx++] = m_compute_timer.getMs();
if (m_idx == SIZE) {
m_idx = 0;
}
}
void FrameTimeRecorder::start_frame() {
m_timer.start();
m_compute_timer.start();
float frame_time = m_fps_timer.getSeconds();
m_last_frame_time = (0.9 * m_last_frame_time) + (0.1 * frame_time);
m_fps_timer.start();
}
void FrameTimeRecorder::draw_window(const DmaStats& dma_stats) {
@@ -52,6 +55,8 @@ void FrameTimeRecorder::draw_window(const DmaStats& dma_stats) {
} else {
ImGui::Text("worst: %.1f", worst);
}
ImGui::SameLine();
ImGui::Text("fps-avg: %.1f", 1.f / m_last_frame_time);
ImGui::Separator();
ImGui::PlotLines(
@@ -105,6 +110,20 @@ void OpenGlDebugGui::draw(const DmaStats& dma_stats) {
ImGui::InputText("Dump", m_dump_save_name, 12);
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Frame Rate")) {
ImGui::MenuItem("Enable V-Sync", nullptr, &m_vsync);
ImGui::MenuItem("Disable V-Sync", nullptr, &m_nosync);
ImGui::Separator();
ImGui::Checkbox("Framelimiter", &framelimiter);
ImGui::InputFloat("Target FPS", &m_target_fps_text);
if (ImGui::MenuItem("Apply")) {
target_fps = m_target_fps_text;
}
ImGui::Separator();
ImGui::Checkbox("Accurate Lag Mode", &experimental_accurate_lag);
ImGui::EndMenu();
}
}
ImGui::EndMainMenuBar();
+26 -1
View File
@@ -27,8 +27,10 @@ class FrameTimeRecorder {
private:
float m_frame_times[SIZE] = {0};
float m_last_frame_time = 0;
int m_idx = 0;
Timer m_timer;
Timer m_compute_timer;
Timer m_fps_timer;
bool m_open = true;
bool m_play = true;
@@ -59,6 +61,26 @@ class OpenGlDebugGui {
return false;
}
bool get_nosync_flag() {
if (m_nosync) {
m_nosync = false;
return true;
}
return false;
}
bool get_vsync_flag() {
if (m_vsync) {
m_vsync = false;
return true;
}
return false;
}
bool framelimiter = false;
float target_fps = 60.f;
bool experimental_accurate_lag = false;
private:
FrameTimeRecorder m_frame_timer;
bool m_draw_frame_time = false;
@@ -70,4 +92,7 @@ class OpenGlDebugGui {
bool m_want_screenshot = false;
char m_dump_save_name[256] = "dump.bin";
char m_screenshot_save_name[256] = "screenshot.png";
bool m_vsync = false;
bool m_nosync = false;
float m_target_fps_text = 60.0;
};
@@ -18,12 +18,14 @@ bool looks_like_tfrag_init(const DmaFollower& follow) {
TFragment::TFragment(const std::string& name,
BucketId my_id,
const std::vector<tfrag3::TFragmentTreeKind>& trees,
bool child_mode)
bool child_mode,
int level_id)
: BucketRenderer(name, my_id),
m_child_mode(child_mode),
m_direct_renderer(fmt::format("{}.direct", name), my_id, 1024, DirectRenderer::Mode::NORMAL),
m_buffered_renderer(my_id),
m_tree_kinds(trees) {
m_tree_kinds(trees),
m_level_id(level_id) {
for (auto& buf : m_buffered_data) {
for (auto& x : buf.pad) {
x = 0xff;
@@ -77,6 +79,37 @@ void TFragment::render(DmaFollower& dma,
return;
}
if (m_my_id == BucketId::TFRAG_LEVEL0) {
DmaTransfer transfers[2];
transfers[0] = dma.read_and_advance();
auto next0 = dma.read_and_advance();
assert(next0.size_bytes == 0);
transfers[1] = dma.read_and_advance();
auto next1 = dma.read_and_advance();
assert(next1.size_bytes == 0);
for (int i = 0; i < 2; i++) {
if (transfers[i].size_bytes == 128 * 16) {
if (render_state->use_occlusion_culling) {
render_state->occlusion_vis[i].valid = true;
memcpy(render_state->occlusion_vis[i].data, transfers[i].data, 128 * 16);
}
} else {
assert(transfers[i].size_bytes == 16);
}
}
}
if (dma.current_tag().kind == DmaTag::Kind::CALL) {
// renderer didn't run, let's just get out of here.
for (int i = 0; i < 4; i++) {
dma.read_and_advance();
}
assert(dma.current_tag_offset() == render_state->next_bucket);
return;
}
if (m_extra_debug) {
ImGui::Begin(fmt::format("{} extra", m_name).c_str());
}
@@ -108,6 +141,9 @@ void TFragment::render(DmaFollower& dma,
memcpy(settings.math_camera.data(), &m_buffered_data[0].pad[TFragDataMem::TFragMatrix0 * 16],
64);
settings.tree_idx = 0;
if (render_state->occlusion_vis[m_level_id].valid) {
settings.occlusion_culling = render_state->occlusion_vis[m_level_id].data;
}
for (int i = 0; i < 4; i++) {
settings.planes[i] = m_pc_port_data.planes[i];
@@ -190,7 +226,7 @@ void TFragment::render(DmaFollower& dma,
m_many_level_render.tfrag_level_renderers[i] = std::make_unique<Tfrag3>();
}
if (!m_many_level_render.tie_level_renderers[i]) {
m_many_level_render.tie_level_renderers[i] = std::make_unique<Tie3>("tie", m_my_id);
m_many_level_render.tie_level_renderers[i] = std::make_unique<Tie3>("tie", m_my_id, 0);
}
m_many_level_render.tfrag_level_renderers[i]->setup_for_level(all_kinds, level_names[i],
render_state);
@@ -44,7 +44,8 @@ class TFragment : public BucketRenderer {
TFragment(const std::string& name,
BucketId my_id,
const std::vector<tfrag3::TFragmentTreeKind>& trees,
bool child_mode);
bool child_mode,
int level_id);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
@@ -293,6 +294,7 @@ class TFragment : public BucketRenderer {
BufferedRenderer::Builder m_buffered_renderer;
Tfrag3 m_tfrag3;
std::vector<tfrag3::TFragmentTreeKind> m_tree_kinds;
int m_level_id;
struct HackManyLevels {
static constexpr int NUM_LEVELS = 23;
@@ -260,7 +260,8 @@ void Tfrag3::render_tree(const TfragRenderSettings& settings,
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(UINT32_MAX);
cull_check_all_slow(settings.planes, tree.vis->vis_nodes, m_cache.vis_temp.data());
cull_check_all_slow(settings.planes, tree.vis->vis_nodes, settings.occlusion_culling,
m_cache.vis_temp.data());
int idx_buffer_ptr = make_index_list_from_vis_string(
m_cache.draw_idx_temp.data(), tree.index_list.data(), *tree.draws, m_cache.vis_temp);
+8 -2
View File
@@ -2,7 +2,8 @@
#include "third-party/imgui/imgui.h"
Tie3::Tie3(const std::string& name, BucketId my_id) : BucketRenderer(name, my_id) {
Tie3::Tie3(const std::string& name, BucketId my_id, int level_id)
: BucketRenderer(name, my_id), m_level_id(level_id) {
// regardless of how many we use some fixed max
// we won't actually interp or upload to gpu the unused ones, but we need a fixed maximum so
// indexing works properly.
@@ -427,6 +428,10 @@ void Tie3::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfi
memcpy(settings.math_camera.data(), m_pc_port_data.camera[0].data(), 64);
settings.tree_idx = 0;
if (render_state->occlusion_vis[m_level_id].valid) {
settings.occlusion_culling = render_state->occlusion_vis[m_level_id].data;
}
for (int i = 0; i < 4; i++) {
settings.planes[i] = m_pc_port_data.planes[i];
render_state->camera_planes[i] = m_pc_port_data.planes[i];
@@ -630,7 +635,8 @@ void Tie3::render_tree(int idx,
tree.perf.index_upload = sizeof(u32) * idx_buffer_ptr;
} else {
Timer cull_timer;
cull_check_all_slow(settings.planes, tree.vis->vis_nodes, m_cache.vis_temp.data());
cull_check_all_slow(settings.planes, tree.vis->vis_nodes, settings.occlusion_culling,
m_cache.vis_temp.data());
tree.perf.cull_time.add(cull_timer.getSeconds());
Timer index_timer;
+3 -1
View File
@@ -9,7 +9,7 @@
class Tie3 : public BucketRenderer {
public:
Tie3(const std::string& name, BucketId my_id);
Tie3(const std::string& name, BucketId my_id, int level_id);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
~Tie3();
@@ -105,6 +105,8 @@ class Tie3 : public BucketRenderer {
float m_wind_multiplier = 1.f;
int m_level_id;
static_assert(sizeof(WindWork) == 84 * 16);
enum State : u32 {
@@ -300,9 +300,19 @@ bool sphere_in_view_ref(const math::Vector4f& sphere, const math::Vector4f* plan
// this isn't super efficient, but we spend so little time here it's not worth it to go faster.
void cull_check_all_slow(const math::Vector4f* planes,
const std::vector<tfrag3::VisNode>& nodes,
const u8* level_occlusion_string,
u8* out) {
for (size_t i = 0; i < nodes.size(); i++) {
out[i] = sphere_in_view_ref(nodes[i].bsphere, planes);
if (level_occlusion_string) {
for (size_t i = 0; i < nodes.size(); i++) {
u16 my_id = nodes[i].my_id;
bool not_occluded =
my_id != 0xffff && level_occlusion_string[my_id / 8] & (1 << (7 - (my_id & 7)));
out[i] = not_occluded && sphere_in_view_ref(nodes[i].bsphere, planes);
}
} else {
for (size_t i = 0; i < nodes.size(); i++) {
out[i] = sphere_in_view_ref(nodes[i].bsphere, planes);
}
}
}
@@ -337,7 +347,7 @@ u32 make_index_list_from_vis_string(std::pair<int, int>* group_out,
int run_start_out = 0;
int run_start_in = 0;
for (auto& grp : draw.vis_groups) {
bool vis = grp.vis_idx == 0xffffffff || vis_data[grp.vis_idx];
bool vis = grp.vis_idx_in_pc_bvh == 0xffffffff || vis_data[grp.vis_idx_in_pc_bvh];
if (building_run) {
if (vis) {
idx_buffer_ptr += grp.num;
@@ -10,8 +10,8 @@ struct TfragRenderSettings {
int tree_idx;
float time_of_day_weights[8] = {0};
math::Vector4f planes[4];
bool do_culling = false;
bool debug_culling = false;
const u8* occlusion_culling = nullptr;
// todo occlusion culling string.
};
@@ -43,6 +43,7 @@ void interp_time_of_day_fast(const float weights[8],
void cull_check_all_slow(const math::Vector4f* planes,
const std::vector<tfrag3::VisNode>& nodes,
const u8* level_occlusion_string,
u8* out);
bool sphere_in_view_ref(const math::Vector4f& sphere, const math::Vector4f* planes);
+22 -5
View File
@@ -27,6 +27,7 @@
#include "game/graphics/opengl_renderer/debug_gui.h"
#include "common/util/FileUtil.h"
#include "common/util/compress.h"
#include "common/util/FrameLimiter.h"
namespace {
@@ -53,6 +54,10 @@ struct GraphicsData {
Serializer loaded_dump;
FrameLimiter frame_limiter;
Timer engine_timer;
double last_engine_time = 1. / 60.;
void serialize(Serializer& ser) {
dma_copier.serialize_last_result(ser);
ogl_renderer.serialize(ser);
@@ -235,10 +240,9 @@ void render_game_frame(int width, int height, int lbox_width, int lbox_height) {
std::unique_lock<std::mutex> lock(g_gfx_data->dma_mutex);
// note: there's a timeout here. If the engine is messed up and not sending us frames,
// we still want to run the glfw loop.
got_chain = g_gfx_data->dma_cv.wait_for(lock, std::chrono::milliseconds(20),
got_chain = g_gfx_data->dma_cv.wait_for(lock, std::chrono::milliseconds(50),
[=] { return g_gfx_data->has_data_to_render; });
}
// render that chain.
if (got_chain) {
// we want to serialize before rendering
@@ -272,6 +276,7 @@ void render_game_frame(int width, int height, int lbox_width, int lbox_height) {
// should be fine to remove this mutex if the game actually waits for vsync to call
// send_chain again. but let's be safe for now.
std::unique_lock<std::mutex> lock(g_gfx_data->dma_mutex);
g_gfx_data->engine_timer.start();
g_gfx_data->has_data_to_render = false;
g_gfx_data->sync_cv.notify_all();
}
@@ -383,6 +388,7 @@ static void gl_render_display(GfxDisplay* display) {
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// window size
int width = Gfx::g_global_settings.lbox_w;
int height = Gfx::g_global_settings.lbox_h;
int fbuf_w, fbuf_h;
@@ -396,6 +402,7 @@ static void gl_render_display(GfxDisplay* display) {
int lbox_w = (fbuf_w - width) / 2;
int lbox_h = (fbuf_h - height) / 2;
// render game!
if (g_gfx_data->debug_gui.want_dump_replay()) {
// hack: no letterbox for dump frames, for now.
render_dump_frame(fbuf_w, fbuf_h, 0, 0);
@@ -412,9 +419,21 @@ static void gl_render_display(GfxDisplay* display) {
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
// switch vsync modes, if requested
if (g_gfx_data->debug_gui.get_nosync_flag()) {
glfwSwapInterval(0);
} else if (g_gfx_data->debug_gui.get_vsync_flag()) {
glfwSwapInterval(1);
}
// actual vsync
g_gfx_data->debug_gui.finish_frame();
glfwSwapBuffers(window);
if (g_gfx_data->debug_gui.framelimiter) {
g_gfx_data->frame_limiter.run(g_gfx_data->debug_gui.target_fps,
g_gfx_data->debug_gui.experimental_accurate_lag,
g_gfx_data->last_engine_time);
}
g_gfx_data->debug_gui.start_frame();
if (display->fullscreen_pending()) {
@@ -425,7 +444,6 @@ static void gl_render_display(GfxDisplay* display) {
if (!g_gfx_data->debug_gui.want_dump_replay()) {
std::unique_lock<std::mutex> lock(g_gfx_data->sync_mutex);
g_gfx_data->frame_idx++;
g_gfx_data->sync_cv.notify_all();
}
@@ -445,11 +463,9 @@ u32 gl_vsync() {
if (!g_gfx_data) {
return 0;
}
std::unique_lock<std::mutex> lock(g_gfx_data->sync_mutex);
auto init_frame = g_gfx_data->frame_idx_of_input_data;
g_gfx_data->sync_cv.wait(lock, [=] { return MasterExit || g_gfx_data->frame_idx > init_frame; });
return g_gfx_data->frame_idx & 1;
}
@@ -458,6 +474,7 @@ u32 gl_sync_path() {
return 0;
}
std::unique_lock<std::mutex> lock(g_gfx_data->sync_mutex);
g_gfx_data->last_engine_time = g_gfx_data->engine_timer.getSeconds();
if (!g_gfx_data->has_data_to_render) {
return 0;
}
+57 -2
View File
@@ -86,8 +86,6 @@
;;(spad-vis (the-as (pointer uint128) (+ #x38b0 #x70000000)))
(spad-vis (scratchpad-ptr uint128 :offset VISIBLE_LIST_SCRATCHPAD))
)
;; TODO this is a hack.
(quad-copy! (-> arg0 vis-bits) (-> arg2 all-visible-list) (/ (+ (-> arg2 visible-list-length) 15) 16))
(b! (not *artist-flip-visible*) cfg-5 :delay (nop!))
(nop!)
@@ -132,6 +130,57 @@
(none)
)
(defun add-pc-port-background-data ((dma-buf dma-buffer))
;; loop over levels
(dotimes (lev-idx 2)
(let ((lev (-> *level* level lev-idx))
(dma-start (-> dma-buf base)))
(cond
((= (-> lev status) 'active)
;; the level is active.
(let ((packet (the-as dma-packet (-> dma-buf base))))
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 128))
(set! (-> packet vif0) (new 'static 'vif-tag))
(set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
(set! (-> dma-buf base) (the pointer (&+ packet 16)))
)
(quad-copy! (-> dma-buf base) (-> lev vis-bits) 128)
(&+! (-> dma-buf base) (* 16 128))
)
(else
(let ((packet (the-as dma-packet (-> dma-buf base))))
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 1))
(set! (-> packet vif0) (new 'static 'vif-tag))
(set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
(set! (-> dma-buf base) (the pointer (&+ packet 16)))
)
(set! (-> (the (pointer uint128) (-> dma-buf base))) (the uint128 0))
(&+! (-> dma-buf base) (* 16 1))
)
)
(let ((a3-3 (-> dma-buf base)))
(let ((v1-38 (the-as object (-> dma-buf base))))
(set! (-> (the-as dma-packet v1-38) dma) (new 'static 'dma-tag :id (dma-tag-id next)))
(set! (-> (the-as dma-packet v1-38) vif0) (new 'static 'vif-tag))
(set! (-> (the-as dma-packet v1-38) vif1) (new 'static 'vif-tag))
(set! (-> dma-buf base) (&+ (the-as pointer v1-38) 16))
)
(dma-bucket-insert-tag (-> *display* frames (-> *display* on-screen) frame bucket-group)
(bucket-id tfrag-0)
dma-start
(the-as (pointer dma-tag) a3-3)
)
)
)
)
)
(defun finish-background ()
"Complete the background drawing.
This function should run after the background engine has executed.
@@ -140,6 +189,12 @@
;; set up common VU0 stuff for background.
(background-upload-vu0)
(#when PC_PORT
(add-pc-port-background-data
(-> *display* frames (-> *display* on-screen) frame global-buf)
)
)
;;;;;;;;;;;;;;;;
;; shrubbery
;;;;;;;;;;;;;;;;