[jak2] misc fixes to renderers (#2488)

Fixes decal on tfrag:

![image](https://user-images.githubusercontent.com/48171810/232174352-11153941-e1f9-4472-becb-f266c2a309e5.png)

Sets up jak 2 alpha shrub test settings. They are still too dark, but
there's no longer incorrect alpha test:

![image](https://user-images.githubusercontent.com/48171810/232174590-f9caa6c5-f190-4c78-b720-5f4055490d47.png)

Fix decal on shrub, a feature used in exactly one place in jak 1:

![image](https://user-images.githubusercontent.com/48171810/232174614-8ea65ca5-a183-45f7-ae79-6bf06a937007.png)

Fixed issue with u16 overflow in castle on the alpha channel, causing
flickering. It barely overflowed, which made me suspicious that we had
some error somewhere. But I think that there code is robust against
overflows.
This commit is contained in:
water111 2023-04-14 21:13:45 -04:00 committed by GitHub
parent ba12d804f7
commit c7f2a23abf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 116 additions and 33 deletions

View file

@ -73,7 +73,7 @@ struct MemoryUsageTracker {
void add(MemoryUsageCategory category, u32 size_bytes) { data[category] += size_bytes; } void add(MemoryUsageCategory category, u32 size_bytes) { data[category] += size_bytes; }
}; };
constexpr int TFRAG3_VERSION = 33; constexpr int TFRAG3_VERSION = 34;
// These vertices should be uploaded to the GPU at load time and don't change // These vertices should be uploaded to the GPU at load time and don't change
struct PreloadedVertex { struct PreloadedVertex {

View file

@ -197,7 +197,7 @@ std::vector<level_tools::TextureRemap> extract_bsp_from_level(const ObjectFileDB
dynamic_cast<level_tools::shrub_types::DrawableTreeInstanceShrub*>(draw_tree.get()); dynamic_cast<level_tools::shrub_types::DrawableTreeInstanceShrub*>(draw_tree.get());
ASSERT(as_shrub_tree); ASSERT(as_shrub_tree);
extract_shrub(as_shrub_tree, fmt::format("{}-{}-shrub", dgo_name, i++), extract_shrub(as_shrub_tree, fmt::format("{}-{}-shrub", dgo_name, i++),
bsp_header.texture_remap_table, tex_db, {}, level_data, false); bsp_header.texture_remap_table, tex_db, {}, level_data, false, db.version());
} else if (draw_tree->my_type() == "drawable-tree-collide-fragment" && extract_collision) { } else if (draw_tree->my_type() == "drawable-tree-collide-fragment" && extract_collision) {
auto as_collide_frags = auto as_collide_frags =
dynamic_cast<level_tools::DrawableTreeCollideFragment*>(draw_tree.get()); dynamic_cast<level_tools::DrawableTreeCollideFragment*>(draw_tree.get());

View file

@ -142,7 +142,8 @@ u32 remap_texture(u32 original, const std::vector<level_tools::TextureRemap>& ma
DrawSettings adgif_to_draw_mode(const AdGifData& ad, DrawSettings adgif_to_draw_mode(const AdGifData& ad,
const TextureDB& tdb, const TextureDB& tdb,
const std::vector<level_tools::TextureRemap>& map, const std::vector<level_tools::TextureRemap>& map,
int count) { int count,
bool alpha_tpage_flag) {
// initialize draw mode // initialize draw mode
DrawMode current_mode; DrawMode current_mode;
current_mode.set_at(true); current_mode.set_at(true);
@ -155,13 +156,25 @@ DrawSettings adgif_to_draw_mode(const AdGifData& ad,
current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_SRC_SRC_SRC); current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_SRC_SRC_SRC);
current_mode.enable_fog(); current_mode.enable_fog();
if (alpha_tpage_flag) {
current_mode.set_alpha_test(DrawMode::AlphaTest::NEVER);
current_mode.set_aref(0);
current_mode.set_alpha_fail(GsTest::AlphaFail::FB_ONLY);
}
// ADGIF 0 // ADGIF 0
bool weird = (u8)ad.tex0_addr != (u32)GsRegisterAddress::TEX0_1; bool weird = (u8)ad.tex0_addr != (u32)GsRegisterAddress::TEX0_1;
if (weird) { if (weird) {
lg::info("---------------- WEIRD: 0x{:x}", ad.tex0_addr); lg::info("---------------- WEIRD: 0x{:x}", ad.tex0_addr);
lg::info("i have {} verts", count); lg::info("i have {} verts", count);
} else { } else {
ASSERT(ad.tex0_data == 0 || ad.tex0_data == 0x800000000); // note: decal?? todo if (ad.tex0_data == 0) {
current_mode.set_decal(false);
} else if (ad.tex0_data == 0x8'0000'0000) {
current_mode.set_decal(true);
} else {
ASSERT(false);
}
} }
// tw/th // tw/th
@ -241,7 +254,8 @@ DrawSettings adgif_to_draw_mode(const AdGifData& ad,
ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto, ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto,
const TextureDB& tdb, const TextureDB& tdb,
const std::vector<level_tools::TextureRemap>& map) { const std::vector<level_tools::TextureRemap>& map,
GameVersion version) {
ShrubProtoInfo result; ShrubProtoInfo result;
for (int frag_idx = 0; frag_idx < proto.generic_geom.length; frag_idx++) { for (int frag_idx = 0; frag_idx < proto.generic_geom.length; frag_idx++) {
auto& frag_out = result.frags.emplace_back(); auto& frag_out = result.frags.emplace_back();
@ -287,7 +301,11 @@ ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto,
ASSERT(3 * (vert_idx + draw.start_vtx_idx) + 3 <= frag.col.size()); ASSERT(3 * (vert_idx + draw.start_vtx_idx) + 3 <= frag.col.size());
} }
draw.settings = adgif_to_draw_mode(ag, tdb, map, count); bool alpha_tpage_flag = false;
if (version > GameVersion::Jak1) {
alpha_tpage_flag = proto.flags & 0x4; // tpage-alpha
}
draw.settings = adgif_to_draw_mode(ag, tdb, map, count, alpha_tpage_flag);
} }
ASSERT(frag.vtx_cnt * 3 * sizeof(u16) <= frag.vtx.size()); ASSERT(frag.vtx_cnt * 3 * sizeof(u16) <= frag.vtx.size());
@ -550,12 +568,13 @@ void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree,
const TextureDB& tex_db, const TextureDB& tex_db,
const std::vector<std::pair<int, int>>& /*expected_missing_textures*/, const std::vector<std::pair<int, int>>& /*expected_missing_textures*/,
tfrag3::Level& out, tfrag3::Level& out,
bool dump_level) { bool dump_level,
GameVersion version) {
auto& tree_out = out.shrub_trees.emplace_back(); auto& tree_out = out.shrub_trees.emplace_back();
auto& protos = tree->info.prototype_inline_array_shrub; auto& protos = tree->info.prototype_inline_array_shrub;
std::vector<ShrubProtoInfo> proto_info; std::vector<ShrubProtoInfo> proto_info;
for (auto& proto : protos.data) { for (auto& proto : protos.data) {
proto_info.push_back(extract_proto(proto, tex_db, map)); proto_info.push_back(extract_proto(proto, tex_db, map, version));
} }
for (auto& arr : tree->discovered_arrays) { for (auto& arr : tree->discovered_arrays) {

View file

@ -24,6 +24,7 @@ void extract_shrub(const level_tools::shrub_types::DrawableTreeInstanceShrub* tr
const TextureDB& tex_db, const TextureDB& tex_db,
const std::vector<std::pair<int, int>>& expected_missing_textures, const std::vector<std::pair<int, int>>& expected_missing_textures,
tfrag3::Level& out, tfrag3::Level& out,
bool dump_level); bool dump_level,
GameVersion version);
} // namespace decompiler } // namespace decompiler

View file

@ -1911,7 +1911,13 @@ void process_draw_mode(std::vector<TFragDraw>& all_draws,
update_mode_from_test1(val, mode); update_mode_from_test1(val, mode);
break; break;
case GsRegisterAddress::TEX0_1: case GsRegisterAddress::TEX0_1:
// ASSERT(val == 0); HACK jak 2 sets this. ASSERT(val == 0 || val == 0x8'0000'0000);
if (val == 0x8'0000'0000) {
mode.set_decal(true);
} else {
mode.set_decal(false);
}
mode.set_tcc(false);
break; break;
case GsRegisterAddress::TEX1_1: case GsRegisterAddress::TEX1_1:
ASSERT(val == 0x120); // some flag ASSERT(val == 0x120); // some flag

View file

@ -34,6 +34,10 @@ SkyBlendHandler::SkyBlendHandler(const std::string& name,
true, true,
level_id) {} level_id) {}
void SkyBlendHandler::init_shaders(ShaderLibrary& shaders) {
m_tfrag_renderer.init_shaders(shaders);
}
void SkyBlendHandler::handle_sky_copies(DmaFollower& dma, void SkyBlendHandler::handle_sky_copies(DmaFollower& dma,
SharedRenderState* render_state, SharedRenderState* render_state,
ScopedProfilerNode& prof) { ScopedProfilerNode& prof) {

View file

@ -19,6 +19,7 @@ class SkyBlendHandler : public BucketRenderer {
std::shared_ptr<SkyBlendCPU> shared_cpu_blender); std::shared_ptr<SkyBlendCPU> shared_cpu_blender);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override; void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override; void draw_debug_window() override;
void init_shaders(ShaderLibrary& shaders) override;
private: private:
void handle_sky_copies(DmaFollower& dma, void handle_sky_copies(DmaFollower& dma,

View file

@ -10,6 +10,10 @@ Shrub::~Shrub() {
discard_tree_cache(); discard_tree_cache();
} }
void Shrub::init_shaders(ShaderLibrary& shaders) {
m_uniforms.decal = glGetUniformLocation(shaders[ShaderId::SHRUB].id(), "decal");
}
void Shrub::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) { void Shrub::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) {
if (!m_enabled) { if (!m_enabled) {
while (dma.current_tag_offset() != render_state->next_bucket) { while (dma.current_tag_offset() != render_state->next_bucket) {
@ -298,6 +302,8 @@ void Shrub::render_tree(int idx,
last_texture = draw.tree_tex_id; last_texture = draw.tree_tex_id;
} }
glUniform1i(m_uniforms.decal, draw.mode.get_decal() ? 1 : 0);
auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::SHRUB); auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::SHRUB);
prof.add_draw_call(); prof.add_draw_call();

View file

@ -13,6 +13,8 @@ class Shrub : public BucketRenderer {
public: public:
Shrub(const std::string& name, int my_id); Shrub(const std::string& name, int my_id);
~Shrub(); ~Shrub();
void init_shaders(ShaderLibrary& shaders) override;
bool setup_for_level(const std::string& level, SharedRenderState* render_state); bool setup_for_level(const std::string& level, SharedRenderState* render_state);
void render_all_trees(const TfragRenderSettings& settings, void render_all_trees(const TfragRenderSettings& settings,
SharedRenderState* render_state, SharedRenderState* render_state,
@ -53,6 +55,10 @@ class Shrub : public BucketRenderer {
} perf; } perf;
}; };
struct {
GLuint decal;
} m_uniforms;
std::vector<Tree> m_trees; std::vector<Tree> m_trees;
std::string m_level_name; std::string m_level_name;
const std::vector<GLuint>* m_textures; const std::vector<GLuint>* m_textures;

View file

@ -145,6 +145,10 @@ void TFragment::draw_debug_window() {
m_tfrag3.draw_debug_window(); m_tfrag3.draw_debug_window();
} }
void TFragment::init_shaders(ShaderLibrary& shaders) {
m_tfrag3.init_shaders(shaders);
}
void TFragment::handle_initialization(DmaFollower& dma) { void TFragment::handle_initialization(DmaFollower& dma) {
// Set up test (different between different renderers) // Set up test (different between different renderers)
auto setup_test = dma.read_and_advance(); auto setup_test = dma.read_and_advance();

View file

@ -43,6 +43,7 @@ class TFragment : public BucketRenderer {
int level_id); int level_id);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override; void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override; void draw_debug_window() override;
void init_shaders(ShaderLibrary& shaders) override;
private: private:
void handle_initialization(DmaFollower& dma); void handle_initialization(DmaFollower& dma);

View file

@ -40,6 +40,10 @@ Tfrag3::~Tfrag3() {
glDeleteVertexArrays(1, &m_debug_vao); glDeleteVertexArrays(1, &m_debug_vao);
} }
void Tfrag3::init_shaders(ShaderLibrary& shaders) {
m_uniforms.decal = glGetUniformLocation(shaders[ShaderId::TFRAG3].id(), "decal");
}
void Tfrag3::update_load(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds, void Tfrag3::update_load(const std::vector<tfrag3::TFragmentTreeKind>& tree_kinds,
const LevelData* loader_data) { const LevelData* loader_data) {
const auto* lev_data = loader_data->level.get(); const auto* lev_data = loader_data->level.get();
@ -188,15 +192,25 @@ void Tfrag3::render_tree(int geom,
return; return;
} }
auto& tree = m_cached_trees.at(geom).at(settings.tree_idx); auto& tree = m_cached_trees.at(geom).at(settings.tree_idx);
const auto* itimes = settings.itimes;
if (tree.freeze_itimes) {
itimes = tree.itimes_debug;
} else {
for (int i = 0; i < 4; i++) {
tree.itimes_debug[i] = settings.itimes[i];
}
}
ASSERT(tree.kind != tfrag3::TFragmentTreeKind::INVALID); ASSERT(tree.kind != tfrag3::TFragmentTreeKind::INVALID);
if (m_color_result.size() < tree.colors->size()) { if (m_color_result.size() < tree.colors->size()) {
m_color_result.resize(tree.colors->size()); m_color_result.resize(tree.colors->size());
} }
if (m_use_fast_time_of_day) { if (m_use_fast_time_of_day) {
interp_time_of_day_fast(settings.itimes, tree.tod_cache, m_color_result.data()); interp_time_of_day_fast(itimes, tree.tod_cache, m_color_result.data());
} else { } else {
interp_time_of_day_slow(settings.itimes, *tree.colors, m_color_result.data()); interp_time_of_day_slow(itimes, *tree.colors, m_color_result.data());
} }
glActiveTexture(GL_TEXTURE10); glActiveTexture(GL_TEXTURE10);
glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture); glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture);
@ -249,6 +263,7 @@ void Tfrag3::render_tree(int geom,
ASSERT(m_textures); ASSERT(m_textures);
glBindTexture(GL_TEXTURE_2D, m_textures->at(draw.tree_tex_id)); glBindTexture(GL_TEXTURE_2D, m_textures->at(draw.tree_tex_id));
auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::TFRAG3); auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::TFRAG3);
glUniform1i(m_uniforms.decal, draw.mode.get_decal() ? 1 : 0);
tree.tris_this_frame += draw.num_triangles; tree.tris_this_frame += draw.num_triangles;
tree.draws_this_frame++; tree.draws_this_frame++;
@ -347,7 +362,12 @@ void Tfrag3::draw_debug_window() {
ImGui::Checkbox("cull debug (slow)", &tree.cull_debug); ImGui::Checkbox("cull debug (slow)", &tree.cull_debug);
ImGui::PopID(); ImGui::PopID();
if (tree.rendered_this_frame) { if (tree.rendered_this_frame) {
ImGui::Checkbox("freeze itimes", &tree.freeze_itimes);
ImGui::Text(" tris: %d draws: %d", tree.tris_this_frame, tree.draws_this_frame); ImGui::Text(" tris: %d draws: %d", tree.tris_this_frame, tree.draws_this_frame);
for (int j = 0; j < 4; j++) {
ImGui::Text(" itimes[%d] 0x%x 0x%x 0x%x 0x%x", j, tree.itimes_debug[j][0],
tree.itimes_debug[j][1], tree.itimes_debug[j][2], tree.itimes_debug[j][3]);
}
} }
} }
} }

View file

@ -13,6 +13,7 @@ class Tfrag3 {
Tfrag3(); Tfrag3();
~Tfrag3(); ~Tfrag3();
void init_shaders(ShaderLibrary& shaders);
void render_all_trees(int geom, void render_all_trees(int geom,
const TfragRenderSettings& settings, const TfragRenderSettings& settings,
SharedRenderState* render_state, SharedRenderState* render_state,
@ -78,8 +79,15 @@ class Tfrag3 {
bool allowed = true; bool allowed = true;
bool forced = false; bool forced = false;
bool cull_debug = false; bool cull_debug = false;
bool freeze_itimes = false;
math::Vector<s32, 4> itimes_debug[4];
}; };
struct {
GLuint decal;
} m_uniforms;
struct Cache { struct Cache {
std::vector<u8> vis_temp; std::vector<u8> vis_temp;
std::vector<std::pair<int, int>> draw_idx_temp; std::vector<std::pair<int, int>> draw_idx_temp;

View file

@ -218,6 +218,7 @@ void interp_time_of_day_slow(const math::Vector<s32, 4> itimes[4],
} }
// result += in[color].rgba[component].cast<float>() * weights[component]; // result += in[color].rgba[component].cast<float>() * weights[component];
} }
result[0] = std::min(result[0], 255.f); result[0] = std::min(result[0], 255.f);
result[1] = std::min(result[1], 255.f); result[1] = std::min(result[1], 255.f);
result[2] = std::min(result[2], 255.f); result[2] = std::min(result[2], 255.f);
@ -349,15 +350,15 @@ void interp_time_of_day_fast(const math::Vector<s32, 4> itimes[4],
color7 = _mm_mullo_epi16(color7, weights7); color7 = _mm_mullo_epi16(color7, weights7);
// add. This order minimizes dependencies. // add. This order minimizes dependencies.
color0 = _mm_add_epi16(color0, color1); color0 = _mm_adds_epi16(color0, color1);
color2 = _mm_add_epi16(color2, color3); color2 = _mm_adds_epi16(color2, color3);
color4 = _mm_add_epi16(color4, color5); color4 = _mm_adds_epi16(color4, color5);
color6 = _mm_add_epi16(color6, color7); color6 = _mm_adds_epi16(color6, color7);
color0 = _mm_add_epi16(color0, color2); color0 = _mm_adds_epi16(color0, color2);
color4 = _mm_add_epi16(color4, color6); color4 = _mm_adds_epi16(color4, color6);
color0 = _mm_add_epi16(color0, color4); color0 = _mm_adds_epi16(color0, color4);
// divide, because we multiplied our weights by 2^7. // divide, because we multiplied our weights by 2^7.
color0 = _mm_srli_epi16(color0, 6); color0 = _mm_srli_epi16(color0, 6);
@ -404,15 +405,15 @@ void interp_time_of_day_fast(const math::Vector<s32, 4> itimes[4],
color7 = _mm_mullo_epi16(color7, weights7); color7 = _mm_mullo_epi16(color7, weights7);
// add. This order minimizes dependencies. // add. This order minimizes dependencies.
color0 = _mm_add_epi16(color0, color1); color0 = _mm_adds_epi16(color0, color1);
color2 = _mm_add_epi16(color2, color3); color2 = _mm_adds_epi16(color2, color3);
color4 = _mm_add_epi16(color4, color5); color4 = _mm_adds_epi16(color4, color5);
color6 = _mm_add_epi16(color6, color7); color6 = _mm_adds_epi16(color6, color7);
color0 = _mm_add_epi16(color0, color2); color0 = _mm_adds_epi16(color0, color2);
color4 = _mm_add_epi16(color4, color6); color4 = _mm_adds_epi16(color4, color6);
color0 = _mm_add_epi16(color0, color4); color0 = _mm_adds_epi16(color0, color4);
// divide, because we multiplied our weights by 2^7. // divide, because we multiplied our weights by 2^7.
color0 = _mm_srli_epi16(color0, 6); color0 = _mm_srli_epi16(color0, 6);

View file

@ -10,6 +10,7 @@ uniform mat4 camera;
uniform float fog_constant; uniform float fog_constant;
uniform float fog_min; uniform float fog_min;
uniform float fog_max; uniform float fog_max;
uniform int decal;
layout (binding = 10) uniform sampler1D tex_T1; // note, sampled in the vertex shader on purpose. layout (binding = 10) uniform sampler1D tex_T1; // note, sampled in the vertex shader on purpose.
out vec4 fragment_color; out vec4 fragment_color;
@ -72,6 +73,10 @@ void main() {
fragment_color *= tod_color * 4; fragment_color *= tod_color * 4;
fragment_color.a *= 2; fragment_color.a *= 2;
if (decal == 1) {
fragment_color.xyz = vec3(1.0, 1.0, 1.0);
}
tex_coord = tex_coord_in; tex_coord = tex_coord_in;
tex_coord.xy /= 4096; tex_coord.xy /= 4096;
} }

View file

@ -64,14 +64,15 @@ void main() {
transformed.y *= SCISSOR_ADJUST * HEIGHT_SCALE; transformed.y *= SCISSOR_ADJUST * HEIGHT_SCALE;
gl_Position = transformed; gl_Position = transformed;
// time of day lookup
fragment_color = texelFetch(tex_T1, time_of_day_index, 0);
// color adjustment
fragment_color *= 2;
fragment_color.a *= 2;
if (decal == 1) { if (decal == 1) {
fragment_color = vec4(1.0, 1.0, 1.0, 1.0); // tfrag/tie always use TCC=RGB, so even with decal, alpha comes from fragment.
} else { fragment_color.xyz = vec3(1.0, 1.0, 1.0);
// time of day lookup
fragment_color = texelFetch(tex_T1, time_of_day_index, 0);
// color adjustment
fragment_color *= 2;
fragment_color.a *= 2;
} }
// fog hack // fog hack