mirror of
https://github.com/open-goal/jak-project
synced 2026-08-16 13:17:34 -04:00
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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
;;;;;;;;;;;;;;;;
|
||||
|
||||
Reference in New Issue
Block a user