Files
mitch030504--Wiicompiled_VR…/aurora-main/lib/dolphin/gx/GXAurora.cpp
T
iChris4 3c20f677eb 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.
2026-09-28 23:21:01 +02:00

251 lines
10 KiB
C++

#include "dolphin/gx/GXAurora.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include "__gx.h"
#include "gx.hpp"
#include "../../window.hpp"
#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.
extern "C" void aurora_clear_native_wheel_vertices() {
// Clearing an empty set reports nothing, so a host that clears both before and after a frame's draws keeps
// that frame's count.
if (aurora::gx::nativeWheelArrays.empty()) return;
aurora::gx::fifo::drain();
aurora::gx::nativeWheelLastMatches.store(aurora::gx::nativeWheelMatches);
aurora::gx::nativeWheelMatches = 0;
aurora::gx::nativeWheelPreviousSources.clear();
for (const auto& array : aurora::gx::nativeWheelArrays)
aurora::gx::nativeWheelPreviousSources.push_back(array.source);
aurora::gx::native_wheel_report();
aurora::gx::nativeWheelArrays.clear();
aurora::gx::nativeWheelLastDecision = nullptr;
aurora::gx::nativeWheelLastDrawCommand = nullptr;
}
extern "C" uint32_t aurora_native_wheel_draw_count() { return aurora::gx::nativeWheelLastMatches.load(); }
extern "C" void aurora_set_native_wheel_vertices(const void* source, const void* replacement, uint32_t size,
const float* modelView) {
if (!source || !replacement || !modelView || !size || size > 65536) return;
aurora::gx::NativeWheelArray array;
array.source = source;
const auto* bytes = static_cast<const uint8_t*>(replacement);
array.bytes.assign(bytes, bytes + size);
std::memcpy(array.modelView.data(), modelView, sizeof(float) * 12);
aurora::gx::nativeWheelArrays.push_back(std::move(array));
// The vector may have moved its elements, and a set changes what a draw resolves to in any case, so the decision
// the merge test compares against is dropped rather than left pointing into the old storage.
aurora::gx::nativeWheelLastDecision = nullptr;
aurora::gx::nativeWheelLastDrawCommand = nullptr;
}
// Single definition for the `Log` that gx.hpp declares for this directory.
aurora::Module Log("aurora::gx");
namespace aurora::gx {
// Called as a set is cleared: a line at about half a second, five seconds and
// a minute of sets, with the matrices on the first report that saw a draw.
void native_wheel_report() {
auto& diagnostics=nativeWheelDiagnostics;
++diagnostics.sets;
++nativeWheelClears;
if(nativeWheelClears!=30 && nativeWheelClears!=300 && nativeWheelClears!=3600) return;
::Log.info("Native steering wheel: {} sets; draws binding a replaced array {} ({} with a larger range, {} outside a "
"set); matched {}; closest position matrix off by {} ({} matrices)",
diagnostics.sets,diagnostics.boundDraws,diagnostics.oversizeDraws,diagnostics.outsideDraws,
diagnostics.matchedDraws,diagnostics.bestError,diagnostics.bestIndexed?"indexed":"current");
if(diagnostics.boundDraws!=0 && nativeWheelReports++==0) {
const auto& m=diagnostics.bestMatrix;
const auto& e=diagnostics.expected;
::Log.info("Native steering wheel: closest [{} {} {} {} | {} {} {} {} | {} {} {} {}] expected [{} {} {} {} | {} {} "
"{} {} | {} {} {} {}]",
m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7],m[8],m[9],m[10],m[11],
e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11]);
}
diagnostics={};
}
} // namespace aurora::gx
static void GXWriteString(const char* label) {
auto length = strlen(label);
if (length > std::numeric_limits<u16>::max()) {
Log.warn("Debug marker size over u16 max, truncating");
length = std::numeric_limits<u16>::max();
}
GX_WRITE_U16(length);
GX_WRITE_DATA(label, length);
}
void GXPushDebugGroup(const char* label) {
GX_WRITE_AURORA(GX_LOAD_AURORA_DEBUG_GROUP_PUSH);
GXWriteString(label);
}
void GXPopDebugGroup() { GX_WRITE_AURORA(GX_LOAD_AURORA_DEBUG_GROUP_POP); }
void GXInsertDebugMarker(const char* label) {
GX_WRITE_AURORA(GX_LOAD_AURORA_DEBUG_MARKER_INSERT);
GXWriteString(label);
}
void AuroraSetViewportPolicy(AuroraViewportPolicy policy) {
const bool changed = g_gxState.viewportPolicy != policy;
if (changed) {
// Finish commands using the old framebuffer mapping before changing it.
aurora::gx::fifo::drain();
}
g_gxState.viewportPolicy = policy;
aurora::window::set_frame_buffer_aspect_fit(policy == AURORA_VIEWPORT_FIT);
aurora::window::set_present_surface_fill(policy == AURORA_VIEWPORT_STRETCH);
if (changed) {
// Reapply the guest viewport and scissor after a resize.
aurora::gx::set_logical_viewport(g_gxState.logicalViewport);
aurora::gx::set_logical_scissor(g_gxState.logicalScissor);
}
}
void AuroraGetRenderSize(u32* width, u32* height) {
// The guest CPU thread uses this safe render-size value outside render passes.
const auto renderSize = aurora::gfx::get_frame_buffer_size();
if (width != nullptr) {
*width = renderSize.x;
}
if (height != nullptr) {
*height = renderSize.y;
}
}
void AuroraGetSurfaceSize(u32* width, u32* height) {
const auto windowSize = aurora::window::get_window_size();
if (width != nullptr) {
*width = windowSize.native_fb_width;
}
if (height != nullptr) {
*height = windowSize.native_fb_height;
}
}
void GXSetViewportRender(f32 left, f32 top, f32 wd, f32 ht, f32 nearz, f32 farz) {
GX_WRITE_AURORA(GX_LOAD_AURORA_VIEWPORT_RENDER);
GX_WRITE_F32(left);
GX_WRITE_F32(top);
GX_WRITE_F32(wd);
GX_WRITE_F32(ht);
GX_WRITE_F32(nearz);
GX_WRITE_F32(farz);
}
void GXSetScissorRender(u32 left, u32 top, u32 wd, u32 ht) {
GX_WRITE_AURORA(GX_LOAD_AURORA_SCISSOR_RENDER);
GX_WRITE_U32(left);
GX_WRITE_U32(top);
GX_WRITE_U32(wd);
GX_WRITE_U32(ht);
}
namespace {
void WriteMappedRenderState(const aurora::gx::MappedRenderState& mapped) {
GXSetViewportRender(mapped.viewport.left, mapped.viewport.top, mapped.viewport.width, mapped.viewport.height,
mapped.viewport.znear, mapped.viewport.zfar);
GXSetScissorRender(static_cast<u32>(std::max(mapped.scissor.x, 0)),
static_cast<u32>(std::max(mapped.scissor.y, 0)),
static_cast<u32>(std::max(mapped.scissor.width, 0)),
static_cast<u32>(std::max(mapped.scissor.height, 0)));
}
} // namespace
void GXSetViewportScissorRenderSafeArea(f32 aspect) {
const auto [targetWidth, targetHeight] = aurora::gfx::get_render_target_size();
if (targetWidth == 0 || targetHeight == 0 || !std::isfinite(aspect) || aspect <= 0.0f) {
return;
}
// Apply queued viewport changes before direct layout draws use the safe area.
aurora::gx::fifo::drain();
auto mapped = aurora::gx::map_logical_render_state();
const float targetAspect = static_cast<float>(targetWidth) / static_cast<float>(targetHeight);
float safeLeft = 0.0f;
float safeTop = 0.0f;
float safeWidth = static_cast<float>(targetWidth);
float safeHeight = static_cast<float>(targetHeight);
if (targetAspect > aspect) {
safeWidth = safeHeight * aspect;
safeLeft = (static_cast<float>(targetWidth) - safeWidth) * 0.5f;
} else if (targetAspect < aspect) {
safeHeight = safeWidth / aspect;
safeTop = (static_cast<float>(targetHeight) - safeHeight) * 0.5f;
}
const float scaleX = safeWidth / static_cast<float>(targetWidth);
const float scaleY = safeHeight / static_cast<float>(targetHeight);
mapped.viewport.left = safeLeft + mapped.viewport.left * scaleX;
mapped.viewport.top = safeTop + mapped.viewport.top * scaleY;
mapped.viewport.width *= scaleX;
mapped.viewport.height *= scaleY;
const float scissorLeft = safeLeft + static_cast<float>(mapped.scissor.x) * scaleX;
const float scissorTop = safeTop + static_cast<float>(mapped.scissor.y) * scaleY;
const float scissorRight =
safeLeft + static_cast<float>(mapped.scissor.x + mapped.scissor.width) * scaleX;
const float scissorBottom =
safeTop + static_cast<float>(mapped.scissor.y + mapped.scissor.height) * scaleY;
const int32_t left = std::clamp(static_cast<int32_t>(std::floor(scissorLeft)), 0,
static_cast<int32_t>(targetWidth));
const int32_t top = std::clamp(static_cast<int32_t>(std::floor(scissorTop)), 0,
static_cast<int32_t>(targetHeight));
const int32_t right = std::clamp(static_cast<int32_t>(std::ceil(scissorRight)), left,
static_cast<int32_t>(targetWidth));
const int32_t bottom = std::clamp(static_cast<int32_t>(std::ceil(scissorBottom)), top,
static_cast<int32_t>(targetHeight));
mapped.scissor = {left, top, right - left, bottom - top};
WriteMappedRenderState(mapped);
}
void GXRestoreViewportScissorRender() {
// Run queued GX draws before leaving the direct layout safe area.
aurora::gx::fifo::drain();
WriteMappedRenderState(aurora::gx::map_logical_render_state());
}
void GXSetTexCopySrcRender(u16 left, u16 top, u16 wd, u16 ht) {
aurora::gx::g_gxState.texCopySrc = {left, top, wd, ht};
aurora::gx::g_gxState.texCopySrcRenderSpace = true;
}
void GXCreateFrameBuffer(u32 width, u32 height) {
aurora::gx::fifo::drain();
aurora::gfx::begin_offscreen(width, height);
}
void GXRestoreFrameBuffer() {
aurora::gx::fifo::drain();
aurora::gfx::end_offscreen();
}