From f5453b96e4dc585fc684345d0da4a2b0727f1e1d Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Thu, 12 Jun 2025 00:51:03 -0400 Subject: [PATCH] [gfx] Fix eye-related crash, clear color logic (#3957) When building the eye texture, the background is first set to the top corner of the iris texture. A long, long time ago, I implemented this by peeking at the data in the texture itself. This doesn't work if the iris texture is animated since the texture will only on the GPU. To fix this, this PR changes the eye renderer to draw a square over the entire eye texture using iris texture's top corner, avoiding the need for getting the texture data on the CPU. I don't remember why I didn't do this in the first place, but this seems better in every way. Also `input_data` in TexturePool not being initialized, which was leading to hard-to-debug crashes when it was randomly initialized to a sometimes invalid but non-null pointer. Co-authored-by: water111 --- game/graphics/opengl_renderer/EyeRenderer.cpp | 53 +++++++++++++------ game/graphics/opengl_renderer/EyeRenderer.h | 5 +- game/graphics/texture/TexturePool.h | 2 +- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/game/graphics/opengl_renderer/EyeRenderer.cpp b/game/graphics/opengl_renderer/EyeRenderer.cpp index c4cca5c2e7..20aabd95d3 100644 --- a/game/graphics/opengl_renderer/EyeRenderer.cpp +++ b/game/graphics/opengl_renderer/EyeRenderer.cpp @@ -256,16 +256,6 @@ std::vector EyeRenderer::get_draws(DmaFollower& dma pair_idx = y0 / SINGLE_EYE_SIZE; l_draw.pair = pair_idx; r_draw.pair = pair_idx; - if (tex0 && tex0->get_data_ptr()) { - u32 tex_val; - memcpy(&tex_val, tex0->get_data_ptr(), 4); - l_draw.clear_color = tex_val; - r_draw.clear_color = tex_val; - } else { - fmt::print("clear lookup failed\n"); - l_draw.clear_color = 0; - r_draw.clear_color = 0; - } } // up next is the pupil background @@ -462,6 +452,33 @@ int add_draw_to_buffer_64(int idx, return idx; } +int add_clear_draw_to_buffer(int idx, float* data) { + // the entire eye texture is cleared using the 0,0 value from the iris texture + const float center = 768; + const float upper = center + 256; + const float lower = center - 256; + data[idx++] = lower; + data[idx++] = lower; + data[idx++] = 0; + data[idx++] = 0; + + data[idx++] = upper; + data[idx++] = lower; + data[idx++] = 0; + data[idx++] = 0; + + data[idx++] = lower; + data[idx++] = upper; + data[idx++] = 0; + data[idx++] = 0; + + data[idx++] = upper; + data[idx++] = upper; + data[idx++] = 0; + data[idx++] = 0; + return idx; +} + void EyeRenderer::run_gpu(const std::vector& draws, SharedRenderState* render_state) { if (draws.empty()) { @@ -474,6 +491,7 @@ void EyeRenderer::run_gpu(const std::vector& draws, // the first thing we'll do is prepare the vertices int buffer_idx = 0; for (const auto& draw : draws) { + buffer_idx = add_clear_draw_to_buffer(buffer_idx, m_gpu_vertex_buffer); if (draw.using_64) { buffer_idx = add_draw_to_buffer_64(buffer_idx, draw.iris, m_gpu_vertex_buffer, draw.pair, draw.lr); @@ -516,13 +534,18 @@ void EyeRenderer::run_gpu(const std::vector& draws, out_tex.fnv_name_hash = draw.fnv_name_hash; out_tex.lr = draw.lr; - // first, the clear - float clear[4] = {0, 0, 0, 0}; - for (int i = 0; i < 4; i++) { - clear[i] = ((draw.clear_color >> (8 * i)) & 0xff) / 255.f; - } + // clear: not really needed, but we do it to help debugging in case all the textures are missing + float clear[4] = {1.0, 0, 0, 0}; glClearBufferfv(GL_COLOR, 0, clear); + // background + if (draw.iris_tex) { + glDisable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, draw.iris_gl_tex); + glDrawArrays(GL_TRIANGLE_STRIP, buffer_idx / 4, 4); + } + buffer_idx += 4 * 4; + // iris if (draw.iris_tex) { // set alpha diff --git a/game/graphics/opengl_renderer/EyeRenderer.h b/game/graphics/opengl_renderer/EyeRenderer.h index 9a1d151826..d59ce4ff6c 100644 --- a/game/graphics/opengl_renderer/EyeRenderer.h +++ b/game/graphics/opengl_renderer/EyeRenderer.h @@ -61,8 +61,8 @@ class EyeRenderer : public BucketRenderer { GpuEyeTex() : fb(128, 128, GL_UNSIGNED_INT_8_8_8_8_REV) {} } m_gpu_eye_textures[NUM_EYE_PAIRS * 2]; - // xyst per vertex, 4 vertices per square, 3 draws per eye, 11 pairs of eyes, 2 eyes per pair. - static constexpr int VTX_BUFFER_FLOATS = 4 * 4 * 3 * NUM_EYE_PAIRS * 2; + // xyst per vertex, 4 vertices per square, 4 draws per eye, 11 pairs of eyes, 2 eyes per pair. + static constexpr int VTX_BUFFER_FLOATS = 4 * 4 * 4 * NUM_EYE_PAIRS * 2; float m_gpu_vertex_buffer[VTX_BUFFER_FLOATS]; GLuint m_vao; GLuint m_gl_vertex_buffer; @@ -74,7 +74,6 @@ class EyeRenderer : public BucketRenderer { bool using_64 = false; int tex_slot() const { return pair * 2 + lr; } - u32 clear_color; EyeDraw iris; GpuTexture* iris_tex = nullptr; u64 iris_gl_tex = 0; diff --git a/game/graphics/texture/TexturePool.h b/game/graphics/texture/TexturePool.h index 3b656fa8a0..c4d320a270 100644 --- a/game/graphics/texture/TexturePool.h +++ b/game/graphics/texture/TexturePool.h @@ -202,7 +202,7 @@ struct TextureInput { GLuint gpu_texture = -1; bool common = false; - const u8* src_data; + const u8* src_data = nullptr; u16 w, h; };