Implement Bullet Bill model visibility and cockpit recentering improvements

- Added support for hiding model arrays in the rendering pipeline to optimize performance during VR gameplay.
- Introduced new functions in `gx_model_visibility` to manage hidden model arrays and their visibility based on game state.
- Enhanced cockpit recentering logic to ensure accurate seat measurements and eye positioning during gameplay.
- Updated `FirstPersonState` to track cockpit height and forward direction, improving VR experience.
- Added tests for Bullet Bill model visibility and cockpit height adjustments to ensure functionality and stability.
- Refactored existing code to accommodate new features and improve overall code organization.
This commit is contained in:
iChris4 committed 2026-09-28 23:21:01 +02:00
1 parent 8c5a7126da
commit 3c20f677eb
17 files changed
+592 -38

No files matched your search

+5
View File
@@ -342,6 +342,11 @@ void aurora_clear_native_wheel_vertices(void);
void aurora_set_native_wheel_vertices(const void* source, const void* replacement, uint32_t size,
const float* modelView);
uint32_t aurora_native_wheel_draw_count(void);
// GX thread, ordered with draws. Hide only rigid draws binding this array at
// this instance's model-view transform. Clear at the end of each guest frame.
void aurora_clear_hidden_model_arrays(void);
void aurora_hide_model_array(const void* source, const float* modelView);
uint32_t aurora_hidden_model_draw_count(void);
typedef void (*AuroraFrameWorkerWaitCallback)();
// Called from the producer thread at bounded intervals while Aurora waits for
// the asynchronous frame worker. The callback must not enter Aurora.
+17
View File
@@ -11,6 +11,23 @@
#include "../../gfx/common.hpp"
#include "../../gx/fifo.hpp"
#include "../../gx/native_wheel.hpp"
#include "../../gx/model_visibility.hpp"
extern "C" void aurora_clear_hidden_model_arrays() {
if (aurora::gx::hiddenModelArrays.empty()) return;
aurora::gx::fifo::drain();
aurora::gx::hiddenModelLastDraws.store(aurora::gx::hiddenModelDraws);
aurora::gx::hiddenModelDraws = 0;
aurora::gx::hiddenModelArrays.clear();
}
extern "C" void aurora_hide_model_array(const void* source, const float* modelView) {
if (!source || !modelView) return;
aurora::gx::fifo::drain();
aurora::gx::HiddenModelArray hidden{source, {}};
std::memcpy(hidden.modelView.data(), modelView, sizeof(float) * 12);
aurora::gx::hiddenModelArrays.push_back(hidden);
}
extern "C" uint32_t aurora_hidden_model_draw_count() { return aurora::gx::hiddenModelLastDraws.load(); }
// GX-thread entry points for the VR native steering wheel (native_wheel.hpp).
// The runtime posts these in order with the frame's draws.
+7
View File
@@ -6,6 +6,7 @@
#include "dolphin/gx/GXAurora.h"
#include "gx.hpp"
#include "native_wheel.hpp"
#include "model_visibility.hpp"
#include "gx_fmt.hpp"
#include "pipeline.hpp"
#include "shader_info.hpp"
@@ -2367,6 +2368,7 @@ bool submit_raw_draw(GXPrimitive prim, GXVtxFmt fmt, const uint8_t* vertices, ui
// This entry point bypasses process(), so it owns the renderer lock itself.
std::lock_guard gpuLock(aurora::renderer_gpu_mutex());
if (model_array_hidden()) return true;
const gfx::Range vertRange = push_draw_vertices(vertices, vtxCount, vtxSize);
const bool interpolationIdentityActive = frame_interpolation_identity_needed();
const PnMtxUsage matrixUsage = interpolationIdentityActive ? pn_mtx_usage(vertices, vtxCount, vtxSize) : PnMtxUsage{};
@@ -2401,6 +2403,11 @@ static bool handle_draw(u8 cmd, const u8* data, u32& pos, u32 size, bool bigEndi
return false;
}
if (model_array_hidden()) {
pos += totalVtxBytes;
return true;
}
// Push raw vertex data to buffer
const uint8_t* vertices = data + pos;
gfx::Range vertRange = push_draw_vertices(vertices, vtxCount, vtxSize);
+47
View File
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "gx.hpp"
#include <array>
#include <atomic>
#include <cmath>
#include <cstring>
#include <vector>
namespace aurora::gx {
struct HiddenModelArray {
const void* source = nullptr;
std::array<float, 12> modelView{};
};
inline std::vector<HiddenModelArray> hiddenModelArrays;
inline uint32_t hiddenModelDraws = 0;
inline std::atomic<uint32_t> hiddenModelLastDraws{0};
// Called before uploading vertices or merging draws. Matrix-indexed shapes
// are deliberately excluded: Bullet Bill's arms have their own animated joints.
inline bool model_array_hidden() {
if (hiddenModelArrays.empty() || g_gxState.projType != GX_PERSPECTIVE ||
(g_gxState.vtxDesc[GX_VA_POS] != GX_INDEX8 && g_gxState.vtxDesc[GX_VA_POS] != GX_INDEX16) ||
g_gxState.vtxDesc[GX_VA_PNMTXIDX] != GX_NONE || g_gxState.currentPnMtx >= MaxPnMtx)
return false;
const auto* matrix = reinterpret_cast<const float*>(&g_gxState.pnMtx[g_gxState.currentPnMtx].pos);
for (const auto& hidden : hiddenModelArrays) {
if (g_gxState.arrays[GX_VA_POS].data != hidden.source) continue;
bool matches = true;
for (int i = 0; i < 12; ++i) {
uint32_t actual, expected;
std::memcpy(&actual, &matrix[i], 4);
std::memcpy(&expected, &hidden.modelView[i], 4);
if ((actual & 0x7f800000u) == 0x7f800000u || (expected & 0x7f800000u) == 0x7f800000u ||
std::abs(matrix[i] - hidden.modelView[i]) > (i % 4 == 3 ? 0.1f : 0.002f)) {
matches = false;
break;
}
}
if (matches) {
++hiddenModelDraws;
return true;
}
}
return false;
}
} // namespace aurora::gx
+1
View File
@@ -64,6 +64,7 @@ if (AURORA_ENABLE_GX)
eye_pass_plan_test.cpp
foveation_test.cpp
native_wheel_test.cpp
model_visibility_test.cpp
cockpit_geometry_test.cpp
texture_bind_group_cache_key_test.cpp
../lib/gfx/efb_ram_encoder.cpp
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "gx_test_common.hpp"
#include "gx/model_visibility.hpp"
#include <aurora/aurora.h>
namespace {
class HiddenModelTest : public GXFifoTest {
protected:
void SetUp() override {
GXFifoTest::SetUp();
flush_and_capture();
aurora::gx::hiddenModelArrays.clear();
aurora::gx::hiddenModelDraws = 0;
aurora::gx::hiddenModelLastDraws = 0;
auto& state = gxState();
state.lastVtxFmt = GX_VTXFMT0;
state.lastVtxSize = 1;
state.projType = GX_PERSPECTIVE;
state.vtxDesc[GX_VA_POS] = GX_INDEX8;
state.arrays[GX_VA_POS].data = source.data();
state.arrays[GX_VA_POS].size = source.size();
state.arrays[GX_VA_POS].stride = 12;
std::memcpy(pos(), matrix.data(), sizeof(float) * 12);
state.stateDirty = true;
aurora_hide_model_array(source.data(), matrix.data());
}
void TearDown() override { aurora_clear_hidden_model_arrays(); }
float* pos() { return reinterpret_cast<float*>(&gxState().pnMtx[0].pos); }
void draw() { decode_fifo({static_cast<u8>(GX_TRIANGLES), 0, 3, 0, 1, 2}); }
std::array<uint8_t, 36> source{};
std::array<float, 12> matrix{1, 0, 0, 10, 0, 1, 0, 20, 0, 0, 1, 30};
};
TEST_F(HiddenModelTest, LocalBodyIsSkippedBeforeVertexUploadAndClearRestoresIt) {
draw();
EXPECT_TRUE(aurora::gfx::testing::last_pushed_vertices().empty());
aurora_clear_hidden_model_arrays();
EXPECT_EQ(aurora_hidden_model_draw_count(), 1u);
draw();
EXPECT_FALSE(aurora::gfx::testing::last_pushed_vertices().empty());
}
TEST_F(HiddenModelTest, OpponentUsingSameArrayIsDrawn) {
pos()[3] += 100;
draw();
EXPECT_FALSE(aurora::gfx::testing::last_pushed_vertices().empty());
EXPECT_EQ(aurora::gx::hiddenModelDraws, 0u);
}
TEST_F(HiddenModelTest, ArmsAndIndexedJointsAreKept) {
std::array<uint8_t, 36> arms{};
gxState().arrays[GX_VA_POS].data = arms.data();
EXPECT_FALSE(aurora::gx::model_array_hidden());
gxState().arrays[GX_VA_POS].data = source.data();
gxState().vtxDesc[GX_VA_PNMTXIDX] = GX_DIRECT;
EXPECT_FALSE(aurora::gx::model_array_hidden());
}
TEST_F(HiddenModelTest, DirectPositionsOrthographicAndInvalidMatricesAreKept) {
gxState().vtxDesc[GX_VA_POS] = GX_DIRECT;
EXPECT_FALSE(aurora::gx::model_array_hidden());
gxState().vtxDesc[GX_VA_POS] = GX_INDEX8;
gxState().projType = GX_ORTHOGRAPHIC;
EXPECT_FALSE(aurora::gx::model_array_hidden());
gxState().projType = GX_PERSPECTIVE;
pos()[0] = __builtin_nanf("");
EXPECT_FALSE(aurora::gx::model_array_hidden());
}
TEST_F(HiddenModelTest, RawBridgeDrawAlsoSkipsLocalBody) {
const std::array<uint8_t, 3> vertices{0, 1, 2};
ASSERT_TRUE(aurora::gx::fifo::submit_raw_draw(GX_TRIANGLES, GX_VTXFMT0, vertices.data(), 3, vertices.size()));
EXPECT_TRUE(aurora::gfx::testing::last_pushed_vertices().empty());
EXPECT_EQ(aurora::gx::hiddenModelDraws, 1u);
}
} // namespace