diff --git a/game/graphics/opengl_renderer/EyeRenderer.cpp b/game/graphics/opengl_renderer/EyeRenderer.cpp index 3ec866dad..e4238d821 100644 --- a/game/graphics/opengl_renderer/EyeRenderer.cpp +++ b/game/graphics/opengl_renderer/EyeRenderer.cpp @@ -249,7 +249,7 @@ 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->get_data_ptr()) { + if (tex0 && tex0->get_data_ptr()) { u32 tex_val; memcpy(&tex_val, tex0->get_data_ptr(), 4); l_draw.clear_color = tex_val; @@ -280,7 +280,7 @@ std::vector EyeRenderer::get_draws(DmaFollower& dma AdgifHelper adgif1(adgif1_dma.data + 16); auto tex1 = render_state->texture_pool->lookup_gpu_texture(adgif1.tex0().tbp0()); - { + if (tex1 && tex1->get_data_ptr()) { l_draw.pupil = read_eye_draw(dma); r_draw.pupil = read_eye_draw(dma); l_draw.pupil_tex = tex1; diff --git a/game/graphics/opengl_renderer/SkyBlendCPU.cpp b/game/graphics/opengl_renderer/SkyBlendCPU.cpp index 36ebf2ca7..882e53951 100644 --- a/game/graphics/opengl_renderer/SkyBlendCPU.cpp +++ b/game/graphics/opengl_renderer/SkyBlendCPU.cpp @@ -151,13 +151,16 @@ SkyBlendStats SkyBlendCPU::do_sky_blends(DmaFollower& dma, } */ if (tex->get_data_ptr()) { - if (is_first_draw) { - blend_sky_initial_fast(intensity, m_texture_data[buffer_idx].data(), tex->get_data_ptr(), - tex->data_size()); - } else { - blend_sky_fast(intensity, m_texture_data[buffer_idx].data(), tex->get_data_ptr(), - tex->data_size()); + if (m_texture_data[buffer_idx].size() == tex->data_size()) { + if (is_first_draw) { + blend_sky_initial_fast(intensity, m_texture_data[buffer_idx].data(), tex->get_data_ptr(), + m_texture_data[buffer_idx].size()); + } else { + blend_sky_fast(intensity, m_texture_data[buffer_idx].data(), tex->get_data_ptr(), + m_texture_data[buffer_idx].size()); + } } + if (buffer_idx == 0) { if (is_first_draw) { stats.sky_draws++; diff --git a/game/graphics/opengl_renderer/foreground/Merc2.cpp b/game/graphics/opengl_renderer/foreground/Merc2.cpp index 6d2c96b04..b1f79a25b 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.cpp +++ b/game/graphics/opengl_renderer/foreground/Merc2.cpp @@ -8,9 +8,23 @@ Merc2::Merc2(const std::string& name, BucketId my_id) : BucketRenderer(name, my_ glGenBuffers(1, &m_bones_buffer); glBindBuffer(GL_UNIFORM_BUFFER, m_bones_buffer); - glBufferData(GL_UNIFORM_BUFFER, MAX_SHADER_BONES * sizeof(MercMat), nullptr, GL_DYNAMIC_DRAW); + glBufferData(GL_UNIFORM_BUFFER, MAX_SHADER_BONE_VECTORS * sizeof(math::Vector4f), nullptr, + GL_DYNAMIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); + GLint val; + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &val); + if (val <= 16) { + // somehow doubt this can happen, but just in case + m_opengl_buffer_alignment = 1; + } else { + m_opengl_buffer_alignment = val / 16; // number of bone vectors + if (m_opengl_buffer_alignment * 16 != (u32)val) { + ASSERT_MSG(false, + fmt::format("opengl uniform buffer alignment is {}, which is strange\n", val)); + } + } + for (int i = 0; i < MAX_LEVELS; i++) { auto& draws = m_level_draw_buckets.emplace_back(); draws.draws.resize(MAX_DRAWS_PER_LEVEL); @@ -115,48 +129,6 @@ void Merc2::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProf flush_draw_buckets(render_state, prof); } -/*! - * Queue up some bones to be included in the bone buffer. - * Returns the index of the first bone. - */ -u32 Merc2::alloc_bones(int count, float scale) { - ASSERT(count + m_next_free_bone <= MAX_SHADER_BONES); - - u32 first_bone = m_next_free_bone; - - // model should have under 128 bones. - ASSERT(count <= MAX_SKEL_BONES); - - // iterate over each bone we need - for (int i = 0; i < count; i++) { - auto& skel_mat = m_skel_matrix_buffer[i]; - auto& shader_mat = m_shader_matrix_buffer[m_next_free_bone++]; - - // scale the transformation matrix (todo: can we move this to the extraction) - // and copy to the large bone buffer. - for (int j = 0; j < 3; j++) { - shader_mat.tmat[j] = skel_mat.tmat[j] * scale; - } - shader_mat.tmat[3] = skel_mat.tmat[3]; - - for (int j = 0; j < 3; j++) { - shader_mat.nmat[j] = skel_mat.nmat[j]; - } - - // we could include the effect of the perspective matrix here. - // for (int j = 0; j < 3; j++) { - // tbone_buffer[i][j] = vf15.elementwise_multiply(bone_mat[j]); - // tbone_buffer[i][j].w() += p.w() * bone_mat[j].z(); - // tbone_buffer[i][j] *= scale; - // } - // - // tbone_buffer[i][3] = vf15.elementwise_multiply(bone_mat[3]) + - // m_low_memory.perspective[3]; tbone_buffer[i][3].w() += p.w() * bone_mat[3].z(); - } - - return first_bone; -} - u32 Merc2::alloc_lights(const VuLights& lights) { ASSERT(m_next_free_light < MAX_LIGHTS); m_stats.num_lights++; @@ -404,6 +376,55 @@ void Merc2::handle_merc_chain(DmaFollower& dma, } } +/*! + * Queue up some bones to be included in the bone buffer. + * Returns the index of the first bone vector. + */ +u32 Merc2::alloc_bones(int count, float scale) { + u32 first_bone_vector = m_next_free_bone_vector; + ASSERT(count * 8 + first_bone_vector <= MAX_SHADER_BONE_VECTORS); + + // model should have under 128 bones. + ASSERT(count <= MAX_SKEL_BONES); + + // iterate over each bone we need + for (int i = 0; i < count; i++) { + auto& skel_mat = m_skel_matrix_buffer[i]; + auto* shader_mat = &m_shader_bone_vector_buffer[m_next_free_bone_vector]; + int bv = 0; + + // scale the transformation matrix (todo: can we move this to the extraction) + // and copy to the large bone buffer. + for (int j = 0; j < 3; j++) { + shader_mat[bv++] = skel_mat.tmat[j] * scale; + } + shader_mat[bv++] = skel_mat.tmat[3]; + + for (int j = 0; j < 3; j++) { + shader_mat[bv++] = skel_mat.nmat[j]; + } + + // we could include the effect of the perspective matrix here. + // for (int j = 0; j < 3; j++) { + // tbone_buffer[i][j] = vf15.elementwise_multiply(bone_mat[j]); + // tbone_buffer[i][j].w() += p.w() * bone_mat[j].z(); + // tbone_buffer[i][j] *= scale; + // } + // + // tbone_buffer[i][3] = vf15.elementwise_multiply(bone_mat[3]) + + // m_low_memory.perspective[3]; tbone_buffer[i][3].w() += p.w() * bone_mat[3].z(); + + m_next_free_bone_vector += 8; + } + + auto b0 = m_next_free_bone_vector; + m_next_free_bone_vector += m_opengl_buffer_alignment - 1; + m_next_free_bone_vector /= m_opengl_buffer_alignment; + m_next_free_bone_vector *= m_opengl_buffer_alignment; + ASSERT(b0 <= m_next_free_bone_vector); + ASSERT(first_bone_vector + count * 8 <= m_next_free_bone_vector); + return first_bone_vector; +} /*! * Flush a model to draw buckets */ @@ -415,16 +436,16 @@ void Merc2::flush_pending_model(SharedRenderState* render_state, ScopedProfilerN const LevelData* lev = m_current_model->level; const tfrag3::MercModel* model = m_current_model->model; - int bone_count = (model->max_bones + 31) & (~31); // todo - // int bone_count = 128; + int bone_count = model->max_bones + 1; if (m_next_free_light >= MAX_LIGHTS) { fmt::print("MERC2 out of lights, consider increasing MAX_LIGHTS\n"); flush_draw_buckets(render_state, prof); } - if (m_next_free_bone + bone_count > MAX_SHADER_BONES) { - fmt::print("MERC2 out of bones, consider increasing MAX_SHADER_BONES\n"); + if (m_next_free_bone_vector + m_opengl_buffer_alignment + bone_count * 8 > + MAX_SHADER_BONE_VECTORS) { + fmt::print("MERC2 out of bones, consider increasing MAX_SHADER_BONE_VECTORS\n"); flush_draw_buckets(render_state, prof); } @@ -560,11 +581,11 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil int last_tex = -1; int last_light = -1; - m_stats.num_bones_uploaded += m_next_free_bone; + m_stats.num_bones_uploaded += m_next_free_bone_vector; glBindBuffer(GL_UNIFORM_BUFFER, m_bones_buffer); - glBufferSubData(GL_UNIFORM_BUFFER, 0, m_next_free_bone * sizeof(MercMat), - m_shader_matrix_buffer); + glBufferSubData(GL_UNIFORM_BUFFER, 0, m_next_free_bone_vector * sizeof(math::Vector4f), + m_shader_bone_vector_buffer); glBindBuffer(GL_UNIFORM_BUFFER, 0); for (u32 di = 0; di < lev_bucket.next_free_draw; di++) { @@ -591,14 +612,14 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil prof.add_draw_call(); prof.add_tri(draw.num_triangles); - glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_bones_buffer, sizeof(MercMat) * draw.first_bone, - 128 * sizeof(MercMat)); + glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_bones_buffer, + sizeof(math::Vector4f) * draw.first_bone, 128 * sizeof(ShaderMercMat)); glDrawElements(GL_TRIANGLE_STRIP, draw.index_count, GL_UNSIGNED_INT, (void*)(sizeof(u32) * draw.first_index)); } } m_next_free_light = 0; - m_next_free_bone = 0; + m_next_free_bone_vector = 0; m_next_free_level_bucket = 0; } diff --git a/game/graphics/opengl_renderer/foreground/Merc2.h b/game/graphics/opengl_renderer/foreground/Merc2.h index 7d29df4dd..7f345e994 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.h +++ b/game/graphics/opengl_renderer/foreground/Merc2.h @@ -65,14 +65,21 @@ class Merc2 : public BucketRenderer { math::Vector4f nmat[3]; }; + struct ShaderMercMat { + math::Vector4f tmat[4]; + math::Vector4f nmat[3]; + math::Vector4f pad; + }; + static constexpr int MAX_SKEL_BONES = 128; - static constexpr int MAX_SHADER_BONES = 8192; + static constexpr int BONE_VECTORS_PER_BONE = 7; + static constexpr int MAX_SHADER_BONE_VECTORS = 8192; // ?? static constexpr int MAX_LEVELS = 3; static constexpr int MAX_DRAWS_PER_LEVEL = 1024; - MercMat m_shader_matrix_buffer[MAX_SHADER_BONES]; - MercMat m_skel_matrix_buffer[MAX_SKEL_BONES]; + math::Vector4f m_shader_bone_vector_buffer[MAX_SHADER_BONE_VECTORS]; + ShaderMercMat m_skel_matrix_buffer[MAX_SKEL_BONES]; struct { GLuint light_direction[3]; @@ -137,7 +144,8 @@ class Merc2 : public BucketRenderer { std::vector m_level_draw_buckets; u32 m_next_free_level_bucket = 0; - u32 m_next_free_bone = 0; + u32 m_next_free_bone_vector = 0; + size_t m_opengl_buffer_alignment = 0; void flush_draw_buckets(SharedRenderState* render_state, ScopedProfilerNode& prof); }; diff --git a/game/graphics/opengl_renderer/shaders/merc2.vert b/game/graphics/opengl_renderer/shaders/merc2.vert index 7393250a6..fc990dc88 100644 --- a/game/graphics/opengl_renderer/shaders/merc2.vert +++ b/game/graphics/opengl_renderer/shaders/merc2.vert @@ -36,6 +36,7 @@ out float fog; struct MercMatrixData { mat4 X; mat3 R; + vec4 pad; }; layout (std140, binding = 1) uniform ub_bones { diff --git a/game/runtime.cpp b/game/runtime.cpp index 2a7668347..b504fc26b 100644 --- a/game/runtime.cpp +++ b/game/runtime.cpp @@ -86,14 +86,15 @@ void deci2_runner(SystemThreadInterface& iface) { lg::debug("[DECI2] Waiting for EE to register protos"); server.wait_for_protos_ready(); // then allow the server to accept connections - if (!server.init_server()) { - ASSERT_MSG(false, "[DECI2] Server not initialized even if protocols are ready, aborting"); + bool server_ok = server.init_server(); + if (!server_ok) { + lg::error("[DECI2] failed to initialize, REPL will not work.\n"); } lg::debug("[DECI2] Waiting for listener..."); bool saw_listener = false; while (!iface.get_want_exit()) { - if (server.is_client_connected()) { + if (server_ok && server.is_client_connected()) { if (!saw_listener) { lg::debug("[DECI2] Connected!"); }