mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 02:00:14 +02:00
Found on a Quest 3 through Virtual Desktop (VirtualDesktopXR, Vulkan): the race camera's view matched the scene camera exactly and the kart's own wheel animated (228 draws a frame took the copy), but during the race's opening pan no draw took it, the 30-frame fallback latched the VR wheel for that vehicle, and it stayed until the scene changed. The fallback now heals: copies keep being published, the VR wheel stands in only while draws are not taking them, and the vehicle's own wheel returns as soon as they are. Both switches are logged, a few per race. With first_person_rotation "yaw_pitch" or "full" the wheel copy and the hands' wheel geometry now ride in the view's own frame (the kart's orientation around the seat) instead of the level seat, so the wheel no longer tilts against the view on slopes. Aurora logs, after about half a second, five seconds and a minute of copies, how many draws bound a copied array (inside and outside its window), how many matched, and the closest position matrix against the expected one, so a mismatch says whether the array or the matrix differs.
228 lines
8.9 KiB
C++
228 lines
8.9 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"
|
|
|
|
// 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();
|
|
}
|
|
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));
|
|
}
|
|
|
|
// 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();
|
|
}
|