[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; }
};
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
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());
ASSERT(as_shrub_tree);
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) {
auto as_collide_frags =
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,
const TextureDB& tdb,
const std::vector<level_tools::TextureRemap>& map,
int count) {
int count,
bool alpha_tpage_flag) {
// initialize draw mode
DrawMode current_mode;
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.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
bool weird = (u8)ad.tex0_addr != (u32)GsRegisterAddress::TEX0_1;
if (weird) {
lg::info("---------------- WEIRD: 0x{:x}", ad.tex0_addr);
lg::info("i have {} verts", count);
} 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
@ -241,7 +254,8 @@ DrawSettings adgif_to_draw_mode(const AdGifData& ad,
ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto,
const TextureDB& tdb,
const std::vector<level_tools::TextureRemap>& map) {
const std::vector<level_tools::TextureRemap>& map,
GameVersion version) {
ShrubProtoInfo result;
for (int frag_idx = 0; frag_idx < proto.generic_geom.length; frag_idx++) {
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());
}
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());
@ -550,12 +568,13 @@ void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree,
const TextureDB& tex_db,
const std::vector<std::pair<int, int>>& /*expected_missing_textures*/,
tfrag3::Level& out,
bool dump_level) {
bool dump_level,
GameVersion version) {
auto& tree_out = out.shrub_trees.emplace_back();
auto& protos = tree->info.prototype_inline_array_shrub;
std::vector<ShrubProtoInfo> proto_info;
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) {

View file

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

View file

@ -1911,7 +1911,13 @@ void process_draw_mode(std::vector<TFragDraw>& all_draws,
update_mode_from_test1(val, mode);
break;
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;
case GsRegisterAddress::TEX1_1:
ASSERT(val == 0x120); // some flag

View file

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

View file

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

View file

@ -10,6 +10,10 @@ Shrub::~Shrub() {
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) {
if (!m_enabled) {
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;
}
glUniform1i(m_uniforms.decal, draw.mode.get_decal() ? 1 : 0);
auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::SHRUB);
prof.add_draw_call();

View file

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

View file

@ -145,6 +145,10 @@ void TFragment::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) {
// Set up test (different between different renderers)
auto setup_test = dma.read_and_advance();

View file

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

View file

@ -40,6 +40,10 @@ Tfrag3::~Tfrag3() {
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,
const LevelData* loader_data) {
const auto* lev_data = loader_data->level.get();
@ -188,15 +192,25 @@ void Tfrag3::render_tree(int geom,
return;
}
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);
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());
interp_time_of_day_fast(itimes, tree.tod_cache, m_color_result.data());
} 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);
glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture);
@ -249,6 +263,7 @@ void Tfrag3::render_tree(int geom,
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);
glUniform1i(m_uniforms.decal, draw.mode.get_decal() ? 1 : 0);
tree.tris_this_frame += draw.num_triangles;
tree.draws_this_frame++;
@ -347,7 +362,12 @@ void Tfrag3::draw_debug_window() {
ImGui::Checkbox("cull debug (slow)", &tree.cull_debug);
ImGui::PopID();
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);
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();
void init_shaders(ShaderLibrary& shaders);
void render_all_trees(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
@ -78,8 +79,15 @@ class Tfrag3 {
bool allowed = true;
bool forced = false;
bool cull_debug = false;
bool freeze_itimes = false;
math::Vector<s32, 4> itimes_debug[4];
};
struct {
GLuint decal;
} m_uniforms;
struct Cache {
std::vector<u8> vis_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[0] = std::min(result[0], 255.f);
result[1] = std::min(result[1], 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);
// add. This order minimizes dependencies.
color0 = _mm_add_epi16(color0, color1);
color2 = _mm_add_epi16(color2, color3);
color4 = _mm_add_epi16(color4, color5);
color6 = _mm_add_epi16(color6, color7);
color0 = _mm_adds_epi16(color0, color1);
color2 = _mm_adds_epi16(color2, color3);
color4 = _mm_adds_epi16(color4, color5);
color6 = _mm_adds_epi16(color6, color7);
color0 = _mm_add_epi16(color0, color2);
color4 = _mm_add_epi16(color4, color6);
color0 = _mm_adds_epi16(color0, color2);
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.
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);
// add. This order minimizes dependencies.
color0 = _mm_add_epi16(color0, color1);
color2 = _mm_add_epi16(color2, color3);
color4 = _mm_add_epi16(color4, color5);
color6 = _mm_add_epi16(color6, color7);
color0 = _mm_adds_epi16(color0, color1);
color2 = _mm_adds_epi16(color2, color3);
color4 = _mm_adds_epi16(color4, color5);
color6 = _mm_adds_epi16(color6, color7);
color0 = _mm_add_epi16(color0, color2);
color4 = _mm_add_epi16(color4, color6);
color0 = _mm_adds_epi16(color0, color2);
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.
color0 = _mm_srli_epi16(color0, 6);

View file

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

View file

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