Files
jak-project/game/graphics/opengl_renderer/background/Tfrag3.cpp
T
water111 0a511da2ce [jak2] fix flickering and depth writes (#2406)
Fixes some mistakes with merc draw modes. The glass in the palace level
no longer writes to the depth buffer (it's "water"):

![image](https://user-images.githubusercontent.com/48171810/227727825-d6726621-88a8-45a8-9cf3-8d6e9edc3d54.png)

Also fixes the one-frame flickers when level draw orders change. We
might be able to make this more efficient in the future, but this will
at least fix the frame with nothing drawn.
2023-03-25 14:00:54 -04:00

497 lines
19 KiB
C++

#include "Tfrag3.h"
#include "common/log/log.h"
#include "third-party/imgui/imgui.h"
Tfrag3::Tfrag3() {
glGenVertexArrays(1, &m_debug_vao);
glBindVertexArray(m_debug_vao);
glGenBuffers(1, &m_debug_verts);
glBindBuffer(GL_ARRAY_BUFFER, m_debug_verts);
glBufferData(GL_ARRAY_BUFFER, DEBUG_TRI_COUNT * 3 * sizeof(DebugVertex), nullptr,
GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, // location 0 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(DebugVertex), // stride
(void*)offsetof(DebugVertex, position) // offset (0)
);
glVertexAttribPointer(1, // location 1 in the shader
4, // 4 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(DebugVertex), // stride
(void*)offsetof(DebugVertex, rgba) // offset (0)
);
glBindVertexArray(0);
// 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.
m_color_result.resize(TIME_OF_DAY_COLOR_COUNT);
}
Tfrag3::~Tfrag3() {
discard_tree_cache();
glDeleteVertexArrays(1, &m_debug_vao);
}
void Tfrag3::update_load(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds,
const LevelData* loader_data) {
const auto* lev_data = loader_data->level.get();
discard_tree_cache();
for (int geom = 0; geom < GEOM_MAX; ++geom) {
m_cached_trees[geom].clear();
}
size_t time_of_day_count = 0;
size_t vis_temp_len = 0;
size_t max_draws = 0;
size_t max_num_grps = 0;
size_t max_inds = 0;
for (int geom = 0; geom < GEOM_MAX; ++geom) {
for (size_t tree_idx = 0; tree_idx < lev_data->tfrag_trees[geom].size(); tree_idx++) {
const auto& tree = lev_data->tfrag_trees[geom][tree_idx];
if (std::find(tree_kinds.begin(), tree_kinds.end(), tree.kind) != tree_kinds.end()) {
auto& tree_cache = m_cached_trees[geom].emplace_back();
tree_cache.kind = tree.kind;
max_draws = std::max(tree.draws.size(), max_draws);
size_t num_grps = 0;
for (auto& draw : tree.draws) {
num_grps += draw.vis_groups.size();
}
max_num_grps = std::max(max_num_grps, num_grps);
max_inds = std::max(tree.unpacked.indices.size(), max_inds);
time_of_day_count = std::max(tree.colors.size(), time_of_day_count);
u32 verts = tree.packed_vertices.vertices.size();
glGenVertexArrays(1, &tree_cache.vao);
glBindVertexArray(tree_cache.vao);
// glGenBuffers(1, &tree_cache.vertex_buffer);
tree_cache.vertex_buffer = loader_data->tfrag_vertex_data[geom][tree_idx];
tree_cache.vert_count = verts;
tree_cache.draws = &tree.draws; // todo - should we just copy this?
tree_cache.colors = &tree.colors;
tree_cache.vis = &tree.bvh;
tree_cache.index_data = tree.unpacked.indices.data();
tree_cache.tod_cache = swizzle_time_of_day(tree.colors);
tree_cache.draw_mode = tree.use_strips ? GL_TRIANGLE_STRIP : GL_TRIANGLES;
vis_temp_len = std::max(vis_temp_len, tree.bvh.vis_nodes.size());
glBindBuffer(GL_ARRAY_BUFFER, tree_cache.vertex_buffer);
// glBufferData(GL_ARRAY_BUFFER, verts * sizeof(tfrag3::PreloadedVertex),
// nullptr,
// GL_STREAM_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, // location 0 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, x) // offset (0)
);
glVertexAttribPointer(1, // location 1 in the shader
3, // 3 values per vert
GL_FLOAT, // floats
GL_FALSE, // normalized
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, s) // offset (0)
);
glVertexAttribIPointer(2, // location 2 in the shader
1, // 1 values per vert
GL_UNSIGNED_SHORT, // u16
sizeof(tfrag3::PreloadedVertex), // stride
(void*)offsetof(tfrag3::PreloadedVertex, color_index) // offset (0)
);
glGenBuffers(1, &tree_cache.single_draw_index_buffer);
glGenBuffers(1, &tree_cache.index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tree_cache.index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, tree.unpacked.indices.size() * sizeof(u32),
tree.unpacked.indices.data(), GL_STREAM_DRAW);
glGenTextures(1, &tree_cache.time_of_day_texture);
glBindTexture(GL_TEXTURE_1D, tree_cache.time_of_day_texture);
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, TIME_OF_DAY_COLOR_COUNT, 0, GL_RGBA,
GL_UNSIGNED_INT_8_8_8_8, nullptr);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindVertexArray(0);
}
}
}
m_cache.vis_temp.resize(vis_temp_len);
m_cache.multidraw_offset_per_stripdraw.resize(max_draws);
m_cache.multidraw_count_buffer.resize(max_num_grps);
m_cache.multidraw_index_offset_buffer.resize(max_num_grps);
m_cache.draw_idx_temp.resize(max_draws);
m_cache.index_temp.resize(max_inds);
ASSERT(time_of_day_count <= TIME_OF_DAY_COLOR_COUNT);
}
bool Tfrag3::setup_for_level(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds,
const std::string& level,
SharedRenderState* render_state) {
// make sure we have the level data.
Timer tfrag3_setup_timer;
auto lev_data = render_state->loader->get_tfrag3_level(level);
if (!lev_data) {
// not loaded
m_has_level = false;
m_textures = nullptr;
m_level_name = "";
discard_tree_cache();
return false;
}
if (m_has_level && lev_data->load_id != m_load_id) {
m_has_level = false;
m_textures = nullptr;
m_level_name = "";
discard_tree_cache();
return setup_for_level(tree_kinds, level, render_state);
}
m_load_id = lev_data->load_id;
if (m_level_name != level) {
update_load(tree_kinds, lev_data);
m_has_level = true;
m_textures = &lev_data->textures;
m_level_name = level;
} else {
m_has_level = true;
}
if (tfrag3_setup_timer.getMs() > 5) {
lg::info("TFRAG setup: {:.1f}ms", tfrag3_setup_timer.getMs());
}
return m_has_level;
}
void Tfrag3::render_tree(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
if (!m_has_level) {
return;
}
auto& tree = m_cached_trees.at(geom).at(settings.tree_idx);
ASSERT(tree.kind != tfrag3::TFragmentTreeKind::INVALID);
if (m_color_result.size() < tree.colors->size()) {
m_color_result.resize(tree.colors->size());
}
if (m_use_fast_time_of_day) {
interp_time_of_day_fast(settings.itimes, tree.tod_cache, m_color_result.data());
} else {
interp_time_of_day_slow(settings.itimes, *tree.colors, m_color_result.data());
}
glActiveTexture(GL_TEXTURE10);
glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture);
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, tree.colors->size(), GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
m_color_result.data());
first_tfrag_draw_setup(settings, render_state, ShaderId::TFRAG3);
glBindVertexArray(tree.vao);
glBindBuffer(GL_ARRAY_BUFFER, tree.vertex_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
render_state->no_multidraw ? tree.single_draw_index_buffer : tree.index_buffer);
glActiveTexture(GL_TEXTURE0);
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(UINT32_MAX);
cull_check_all_slow(settings.planes, tree.vis->vis_nodes, settings.occlusion_culling,
m_cache.vis_temp.data());
u32 total_tris;
if (render_state->no_multidraw) {
u32 idx_buffer_size = make_index_list_from_vis_string(
m_cache.draw_idx_temp.data(), m_cache.index_temp.data(), *tree.draws, m_cache.vis_temp,
tree.index_data, &total_tris);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size * sizeof(u32), m_cache.index_temp.data(),
GL_STREAM_DRAW);
} else {
total_tris = make_multidraws_from_vis_string(
m_cache.multidraw_offset_per_stripdraw.data(), m_cache.multidraw_count_buffer.data(),
m_cache.multidraw_index_offset_buffer.data(), *tree.draws, m_cache.vis_temp);
}
prof.add_tri(total_tris);
for (size_t draw_idx = 0; draw_idx < tree.draws->size(); draw_idx++) {
const auto& draw = tree.draws->operator[](draw_idx);
const auto& multidraw_indices = m_cache.multidraw_offset_per_stripdraw[draw_idx];
const auto& singledraw_indices = m_cache.draw_idx_temp[draw_idx];
if (render_state->no_multidraw) {
if (singledraw_indices.second == 0) {
continue;
}
} else {
if (multidraw_indices.second == 0) {
continue;
}
}
ASSERT(m_textures);
glBindTexture(GL_TEXTURE_2D, m_textures->at(draw.tree_tex_id));
auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::TFRAG3);
tree.tris_this_frame += draw.num_triangles;
tree.draws_this_frame++;
prof.add_draw_call();
if (render_state->no_multidraw) {
glDrawElements(tree.draw_mode, singledraw_indices.second, GL_UNSIGNED_INT,
(void*)(singledraw_indices.first * sizeof(u32)));
} else {
glMultiDrawElements(tree.draw_mode, &m_cache.multidraw_count_buffer[multidraw_indices.first],
GL_UNSIGNED_INT,
&m_cache.multidraw_index_offset_buffer[multidraw_indices.first],
multidraw_indices.second);
}
switch (double_draw.kind) {
case DoubleDrawKind::NONE:
break;
case DoubleDrawKind::AFAIL_NO_DEPTH_WRITE:
prof.add_draw_call();
glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "alpha_min"),
-10.f);
glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "alpha_max"),
double_draw.aref_second);
glDepthMask(GL_FALSE);
if (render_state->no_multidraw) {
glDrawElements(tree.draw_mode, singledraw_indices.second, GL_UNSIGNED_INT,
(void*)(singledraw_indices.first * sizeof(u32)));
} else {
glMultiDrawElements(
tree.draw_mode, &m_cache.multidraw_count_buffer[multidraw_indices.first],
GL_UNSIGNED_INT, &m_cache.multidraw_index_offset_buffer[multidraw_indices.first],
multidraw_indices.second);
}
break;
default:
ASSERT(false);
}
}
glBindVertexArray(0);
}
/*!
* Render all trees with settings for the given tree.
* This is intended to be used only for debugging when we can't easily get commands for all trees
* working.
*/
void Tfrag3::render_all_trees(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
TfragRenderSettings settings_copy = settings;
for (size_t i = 0; i < m_cached_trees[geom].size(); i++) {
if (m_cached_trees[geom][i].kind != tfrag3::TFragmentTreeKind::INVALID) {
settings_copy.tree_idx = i;
render_tree(geom, settings_copy, render_state, prof);
}
}
}
void Tfrag3::render_matching_trees(int geom,
const std::vector<tfrag3::TFragmentTreeKind>& trees,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
TfragRenderSettings settings_copy = settings;
for (size_t i = 0; i < m_cached_trees[geom].size(); i++) {
auto& tree = m_cached_trees[geom][i];
tree.reset_stats();
if (!tree.allowed) {
continue;
}
if (std::find(trees.begin(), trees.end(), tree.kind) != trees.end() || tree.forced) {
tree.rendered_this_frame = true;
settings_copy.tree_idx = i;
render_tree(geom, settings_copy, render_state, prof);
if (tree.cull_debug) {
render_tree_cull_debug(settings_copy, render_state, prof);
}
}
}
}
void Tfrag3::draw_debug_window() {
for (int i = 0; i < (int)m_cached_trees.at(lod()).size(); i++) {
auto& tree = m_cached_trees.at(lod()).at(i);
if (tree.kind == tfrag3::TFragmentTreeKind::INVALID) {
continue;
}
ImGui::PushID(i);
ImGui::Text("[%d] %10s", i, tfrag3::tfrag_tree_names[(int)m_cached_trees[lod()][i].kind]);
ImGui::SameLine();
ImGui::Checkbox("Allow?", &tree.allowed);
ImGui::SameLine();
ImGui::Checkbox("Force?", &tree.forced);
ImGui::SameLine();
ImGui::Checkbox("cull debug (slow)", &tree.cull_debug);
ImGui::PopID();
if (tree.rendered_this_frame) {
ImGui::Text(" tris: %d draws: %d", tree.tris_this_frame, tree.draws_this_frame);
}
}
}
void Tfrag3::discard_tree_cache() {
m_textures = nullptr;
for (int geom = 0; geom < GEOM_MAX; ++geom) {
for (auto& tree : m_cached_trees[geom]) {
if (tree.kind != tfrag3::TFragmentTreeKind::INVALID) {
glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture);
glDeleteTextures(1, &tree.time_of_day_texture);
glDeleteBuffers(1, &tree.single_draw_index_buffer);
glDeleteBuffers(1, &tree.index_buffer);
glDeleteVertexArrays(1, &tree.vao);
}
}
m_cached_trees[geom].clear();
}
}
namespace {
float frac(float in) {
return in - (int)in;
}
void debug_vis_draw(int first_root,
int tree,
int num,
int depth,
const std::vector<tfrag3::VisNode>& nodes,
std::vector<Tfrag3::DebugVertex>& verts_out) {
for (int ki = 0; ki < num; ki++) {
auto& node = nodes.at(ki + tree - first_root);
ASSERT(node.child_id != 0xffff);
math::Vector4f rgba{frac(0.4 * depth), frac(0.7 * depth), frac(0.2 * depth), 0.06};
math::Vector3f center = node.bsphere.xyz();
float rad = node.bsphere.w();
math::Vector3f corners[8] = {center, center, center, center};
corners[0].x() += rad;
corners[1].x() += rad;
corners[2].x() -= rad;
corners[3].x() -= rad;
corners[0].y() += rad;
corners[1].y() -= rad;
corners[2].y() += rad;
corners[3].y() -= rad;
for (int i = 0; i < 4; i++) {
corners[i + 4] = corners[i];
corners[i].z() += rad;
corners[i + 4].z() -= rad;
}
if (true) {
for (int i : {0, 4}) {
verts_out.push_back({corners[0 + i], rgba});
verts_out.push_back({corners[1 + i], rgba});
verts_out.push_back({corners[2 + i], rgba});
verts_out.push_back({corners[1 + i], rgba}); // 0
verts_out.push_back({corners[3 + i], rgba});
verts_out.push_back({corners[2 + i], rgba});
}
for (int i : {2, 6, 7, 2, 3, 7, 0, 4, 5, 0, 5, 1, 0, 6, 4, 0, 6, 2, 1, 3, 7, 1, 5, 7}) {
verts_out.push_back({corners[i], rgba});
}
constexpr int border0[12] = {0, 4, 6, 2, 2, 6, 3, 7, 0, 1, 2, 3};
constexpr int border1[12] = {1, 5, 7, 3, 0, 4, 1, 5, 4, 5, 6, 7};
rgba.w() = 1.0;
for (int i = 0; i < 12; i++) {
auto p0 = corners[border0[i]];
auto p1 = corners[border1[i]];
auto diff = (p1 - p0).normalized();
math::Vector3f px = diff.z() == 0 ? math::Vector3f{1, 0, 1} : math::Vector3f{0, 1, 1};
auto off = diff.cross(px) * 2000;
verts_out.push_back({p0 + off, rgba});
verts_out.push_back({p0 - off, rgba});
verts_out.push_back({p1 - off, rgba});
verts_out.push_back({p0 + off, rgba});
verts_out.push_back({p1 + off, rgba});
verts_out.push_back({p1 - off, rgba});
}
}
if (node.flags) {
debug_vis_draw(first_root, node.child_id, node.num_kids, depth + 1, nodes, verts_out);
}
}
}
} // namespace
void Tfrag3::render_tree_cull_debug(const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
// generate debug verts:
m_debug_vert_data.clear();
auto& tree = m_cached_trees.at(settings.tree_idx).at(lod());
debug_vis_draw(tree.vis->first_root, tree.vis->first_root, tree.vis->num_roots, 1,
tree.vis->vis_nodes, m_debug_vert_data);
render_state->shaders[ShaderId::TFRAG3_NO_TEX].activate();
glUniformMatrix4fv(
glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3_NO_TEX].id(), "camera"), 1,
GL_FALSE, settings.math_camera.data());
glUniform4f(
glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3_NO_TEX].id(), "hvdf_offset"),
settings.hvdf_offset[0], settings.hvdf_offset[1], settings.hvdf_offset[2],
settings.hvdf_offset[3]);
glUniform1f(
glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3_NO_TEX].id(), "fog_constant"),
settings.fog.x());
// glDisable(GL_DEPTH_TEST);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GEQUAL);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // ?
glDepthMask(GL_FALSE);
glBindVertexArray(m_debug_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_debug_verts);
int remaining = m_debug_vert_data.size();
int start = 0;
while (remaining > 0) {
int to_do = std::min(DEBUG_TRI_COUNT * 3, remaining);
glBufferSubData(GL_ARRAY_BUFFER, 0, to_do * sizeof(DebugVertex),
m_debug_vert_data.data() + start);
glDrawArrays(GL_TRIANGLES, 0, to_do);
prof.add_draw_call();
prof.add_tri(to_do / 3);
remaining -= to_do;
start += to_do;
}
}