mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 01:00:14 +02:00
MKW_VR_STANDALONE (runtime_config.h) names the standalone headsets: the Quest, and the Steam Frame in its Android flavour and its native SteamOS build. They share render scale 0.8, foveation medium, the GX thread, the game's object culling, and the headset panel's foveation and tracked-hands rows; passthrough stays Quest-only. The Frame's native build also sets AuroraConfig::xrHeadsetOnly, so Aurora neither presents the desktop window nobody watches nor renders it past the last pass the eyes sample, which Android does always (4 to 6 ms of a 12 ms GPU frame on a Quest 3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
431 lines
18 KiB
C++
431 lines
18 KiB
C++
#ifndef AURORA_AURORA_H
|
|
#define AURORA_AURORA_H
|
|
|
|
#ifdef __cplusplus
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
extern "C" {
|
|
#else
|
|
#include "stdbool.h"
|
|
#include "stddef.h"
|
|
#include "stdint.h"
|
|
#endif
|
|
|
|
typedef enum {
|
|
BACKEND_AUTO,
|
|
BACKEND_D3D11,
|
|
BACKEND_D3D12,
|
|
BACKEND_METAL,
|
|
BACKEND_VULKAN,
|
|
BACKEND_OPENGL,
|
|
BACKEND_OPENGLES,
|
|
BACKEND_WEBGPU,
|
|
BACKEND_NULL,
|
|
} AuroraBackend;
|
|
|
|
typedef enum {
|
|
LOG_DEBUG,
|
|
LOG_INFO,
|
|
LOG_WARNING,
|
|
LOG_ERROR,
|
|
LOG_FATAL,
|
|
} AuroraLogLevel;
|
|
|
|
typedef enum {
|
|
AURORA_DISPLAY_MODE_WINDOWED,
|
|
AURORA_DISPLAY_MODE_BORDERLESS,
|
|
AURORA_DISPLAY_MODE_EXCLUSIVE,
|
|
} AuroraDisplayMode;
|
|
|
|
typedef struct {
|
|
int32_t x;
|
|
int32_t y;
|
|
} AuroraWindowPos;
|
|
|
|
typedef struct {
|
|
uint32_t width;
|
|
uint32_t height;
|
|
|
|
/**
|
|
* Width of the main GX framebuffer.
|
|
*/
|
|
uint32_t fb_width;
|
|
|
|
/**
|
|
* Height of the main GX framebuffer.
|
|
*/
|
|
uint32_t fb_height;
|
|
|
|
/**
|
|
* The size of the framebuffer used to present to the operating system.
|
|
* May differ from fb_width if Aurora is instructed to force an aspect ratio or resolution configuration.
|
|
*/
|
|
uint32_t native_fb_width;
|
|
|
|
/**
|
|
* The size of the framebuffer used to present to the operating system.
|
|
* May differ from fb_height if Aurora is instructed to force an aspect ratio or resolution configuration.
|
|
*/
|
|
uint32_t native_fb_height;
|
|
float scale;
|
|
} AuroraWindowSize;
|
|
|
|
typedef struct SDL_Window SDL_Window;
|
|
typedef struct AuroraEvent AuroraEvent;
|
|
|
|
typedef void (*AuroraLogCallback)(AuroraLogLevel level, const char* module, const char* message, unsigned int len);
|
|
typedef void (*AuroraImGuiInitCallback)(const AuroraWindowSize* size);
|
|
|
|
enum { AURORA_STEREO_EYE_COUNT = 2 };
|
|
|
|
/**
|
|
* One eye of a stereo frame supplied by the host application.
|
|
*
|
|
* projection is row-major and supplies the OpenXR frustum's X/Y scale and
|
|
* asymmetric-center terms at [0][0], [0][2], [1][1], and [1][2]. Aurora
|
|
* applies those four values to each perspective GX draw while preserving the
|
|
* draw's own depth mapping and renderer depth-range adjustment.
|
|
*
|
|
* viewFromCenter is a row-major affine 3x4 transform from the center-eye view
|
|
* space into this eye's view space. Identity keeps the recorded view and is
|
|
* useful when the game has already applied the eye transform before issuing GX
|
|
* commands. That center-eye space is the game's recorded view space unless
|
|
* aurora_set_stereo_scene_anchor() relocated the camera for the sealed frame,
|
|
* in which case the anchor is composed in for world draws only.
|
|
*
|
|
* Both transforms are ignored in AURORA_STEREO_FRAME_VIRTUAL_SCREEN mode.
|
|
*/
|
|
typedef struct {
|
|
uint32_t width;
|
|
uint32_t height;
|
|
float projection[16];
|
|
float viewFromCenter[12];
|
|
} AuroraStereoEye;
|
|
|
|
typedef enum {
|
|
// Replay perspective GX draws with the supplied per-eye transforms.
|
|
AURORA_STEREO_FRAME_IMMERSIVE_REPLAY = 0,
|
|
// Copy the completed mono present source to both eye outputs. The OpenXR
|
|
// backend can present these images as a compositor quad layer.
|
|
AURORA_STEREO_FRAME_VIRTUAL_SCREEN = 1,
|
|
} AuroraStereoFrameMode;
|
|
|
|
// aurora_end_frame() uses this sentinel when its caller cannot associate a
|
|
// sealed frame with an application safety state. Immersive providers are only
|
|
// accepted through aurora_end_frame_tagged() with an exact matching tag.
|
|
#define AURORA_STEREO_CONTENT_TAG_UNKNOWN UINT64_MAX
|
|
|
|
/**
|
|
* VR cockpit overlay: tracked hands and, when the vehicle's own wheel cannot be
|
|
* animated, a synthetic steering wheel or handlebar. Everything is in metres
|
|
* in a seated frame (+X right, +Y up, -Z forward) whose origin is the headset's
|
|
* immersive base position. Aurora draws it per eye after the scene, depth-tested
|
|
* against the scene with the scene's own depth mapping.
|
|
*/
|
|
enum { AURORA_VR_HAND_JOINT_COUNT = 26 };
|
|
|
|
typedef struct {
|
|
bool tracked;
|
|
bool held;
|
|
// The hand-tracking joints below are valid: the hand is drawn from them
|
|
// instead of curling from squeeze at seatFromGrip.
|
|
bool jointsValid;
|
|
float squeeze;
|
|
float seatFromGrip[12];
|
|
// XR_EXT_hand_tracking joints in XR_HAND_JOINT_* order, each a row-major 3x4
|
|
// in the seated frame, and their radii in metres.
|
|
float seatFromJoint[AURORA_VR_HAND_JOINT_COUNT][12];
|
|
float jointRadii[AURORA_VR_HAND_JOINT_COUNT];
|
|
} AuroraCockpitHand;
|
|
|
|
typedef struct {
|
|
bool active;
|
|
float wheelAngle;
|
|
// The vehicle's own wheel is animated in the scene, so no synthetic wheel is drawn.
|
|
bool nativeWheel;
|
|
bool bike;
|
|
float handlebarRadius;
|
|
// World units per metre used to build this packet's eye transforms.
|
|
float unitsPerMeter;
|
|
float seatFromHandlebar[12];
|
|
float eyeFromSeat[AURORA_STEREO_EYE_COUNT][12];
|
|
AuroraCockpitHand hands[2];
|
|
} AuroraCockpit;
|
|
|
|
// Inventory from the same guest frame as the scene. hand: 0 left, 1 right,
|
|
// 2 off. A zero-initialised value has no item.
|
|
typedef struct {
|
|
uint64_t raceGeneration;
|
|
uint8_t id;
|
|
uint8_t count;
|
|
uint8_t hand;
|
|
bool valid;
|
|
} AuroraCockpitItem;
|
|
|
|
typedef struct {
|
|
float position[3];
|
|
int16_t joints[4];
|
|
float weights[4];
|
|
} AuroraVRHandVertex;
|
|
|
|
/**
|
|
* Shows the headset settings panel (aurora_imgui_set_stereo_overlay) as the
|
|
* OpenXR backend's own compositor quad layer instead of drawing it into the
|
|
* eyes, so the eye resolution no longer limits its text. The backend then asks
|
|
* for the panel image as an extra stereo target. Any thread.
|
|
*/
|
|
void aurora_set_stereo_panel_layer(bool enabled);
|
|
|
|
// Copies optional runtime-provided hand meshes (XR_FB_hand_tracking_mesh, 26
|
|
// joints). Null clears to the procedural glove. Bind poses: x,y,z,w,px,py,pz,
|
|
// in the space of the mesh's vertices, as xrLocateHandJointsEXT reports poses:
|
|
// a hand with tracked joints is skinned with seatFromJoint * inverse(bind).
|
|
void aurora_set_vr_hand_mesh(uint32_t hand, const AuroraVRHandVertex* vertices, uint32_t vertexCount,
|
|
const uint16_t* indices, uint32_t indexCount, const float* bindPoses,
|
|
const int32_t* parents, uint32_t jointCount);
|
|
|
|
/**
|
|
* Stereo data for one sealed GX frame. frameToken is opaque to Aurora and is
|
|
* forwarded unchanged to the internal stereo output sink. contentTag must
|
|
* match the tag latched by aurora_end_frame_tagged() for immersive replay.
|
|
*/
|
|
typedef struct {
|
|
uint64_t frameToken;
|
|
AuroraStereoEye eyes[AURORA_STEREO_EYE_COUNT];
|
|
// Appended to preserve the frameToken/eyes prefix used by older providers.
|
|
AuroraStereoFrameMode mode;
|
|
uint64_t contentTag;
|
|
// Predicted display time converted to std::chrono::steady_clock nanoseconds.
|
|
// Used for diagnostics. Scene playback has its own clock; the eye poses remain
|
|
// predicted for this time even when the runtime looks several game frames ahead.
|
|
uint64_t displayTimeNanos;
|
|
// Optional; inactive when zero-initialised.
|
|
AuroraCockpit cockpit;
|
|
// Immersive replay only: shows the race through a window rather than all
|
|
// around. Each eye keeps what it sees through the 2D layer's screen
|
|
// (aurora_set_stereo_hud_screen's rectangle, on which the 2D layer is then
|
|
// always placed), with premultiplied alpha 1 there, and is transparent black
|
|
// everywhere else, for the host's compositor to show its own background
|
|
// (the room, on a headset with passthrough) around it.
|
|
bool window;
|
|
// Eye-tracked foveation (aurora_set_stereo_foveation), while gazeValid: where the player looks, in
|
|
// tangents of each eye's view (x right, y up, as in the projection's frustum). The immersive eyes'
|
|
// full-density region then centres there instead of on each eye's forward direction.
|
|
float gaze[AURORA_STEREO_EYE_COUNT][2];
|
|
bool gazeValid;
|
|
} AuroraStereoFrame;
|
|
|
|
/**
|
|
* Called on Aurora's frame worker immediately before a GX frame is sealed.
|
|
* Return false to render that logical frame in mono only. The callback must
|
|
* be non-blocking and must not call back into Aurora.
|
|
*/
|
|
typedef bool (*AuroraStereoFrameProvider)(uint32_t logicalFrame, AuroraStereoFrame* frame, void* userdata);
|
|
|
|
// Wake retained stereo replay after publishing a packet. The provider remains
|
|
// non-blocking, and all OpenXR calls stay on the application's pacing thread.
|
|
void aurora_notify_stereo_frame();
|
|
|
|
typedef struct {
|
|
const char* appName;
|
|
const char* userPath;
|
|
const char* cachePath;
|
|
// Read-only application resources. Defaults to SDL_GetBasePath(), which is
|
|
// where release builds place initial_pipeline_cache.db.
|
|
const char* resourcesPath;
|
|
AuroraBackend desiredBackend;
|
|
uint32_t msaa;
|
|
uint16_t maxTextureAnisotropy;
|
|
// No vsync knob exists: the swapchain is always configured for a
|
|
// non-blocking present mode (Immediate, else Mailbox). See best_present_mode.
|
|
bool startFullscreen;
|
|
bool allowJoystickBackgroundEvents;
|
|
bool pauseOnFocusLost;
|
|
bool allowTextureReplacements;
|
|
bool allowTextureDumps;
|
|
bool disableCopyFilter;
|
|
// When false, Aurora centers the first window. When true, windowPosX/Y are restored verbatim,
|
|
// including negative coordinates on monitors left of or above the primary display.
|
|
bool hasWindowPosition;
|
|
int32_t windowPosX;
|
|
int32_t windowPosY;
|
|
uint32_t windowWidth;
|
|
uint32_t windowHeight;
|
|
void* iconRGBA8;
|
|
uint32_t iconWidth;
|
|
uint32_t iconHeight;
|
|
AuroraLogCallback logCallback;
|
|
AuroraLogLevel logLevel;
|
|
AuroraImGuiInitCallback imGuiInitCallback;
|
|
|
|
/*
|
|
* The size of the GameCube's main memory, or MEM1 on the Wii.
|
|
* Note that it will not be allocated at the exact 0x80000000 address, as that cannot be guaranteed.
|
|
* This can be set to 0 to disable allocating this region.
|
|
*/
|
|
uint32_t mem1Size;
|
|
|
|
/*
|
|
* The size of the GameCube's ARAM, or MEM2 on the Wii.
|
|
* This can be set to 0 to disable allocating this region.
|
|
*/
|
|
uint32_t mem2Size;
|
|
|
|
// Optional directory for the portable GX pipeline database. When null, the
|
|
// database is stored in cachePath with Dawn's machine-specific cache.
|
|
const char* pipelineCachePath;
|
|
|
|
// Enables renderer features needed by an external XR compositor. The normal
|
|
// desktop path is unchanged when false.
|
|
bool xrInterop;
|
|
// Asks for fragment density maps on the Vulkan device, for foveated eye
|
|
// rendering (aurora_set_stereo_foveation). Only a Dawn built with Aurora's
|
|
// patches has them (the Quest build). Every render pipeline is then built to
|
|
// run under a density map, so set it only when foveation may be used.
|
|
bool xrFragmentDensityMap;
|
|
// Optional OpenXR-selected D3D adapter. Supplying the runtime's LUID before
|
|
// device creation keeps Dawn and the compositor on the same physical GPU.
|
|
bool hasD3D12AdapterLuid;
|
|
uint32_t d3d12AdapterLuidLow;
|
|
int32_t d3d12AdapterLuidHigh;
|
|
// Nobody watches the desktop window while the headset runs (a standalone
|
|
// headset such as the Steam Frame's native build): skip presenting it and stop
|
|
// the mono render after the last pass the eyes sample, as Android always does.
|
|
bool xrHeadsetOnly;
|
|
} AuroraConfig;
|
|
|
|
typedef struct {
|
|
AuroraBackend backend;
|
|
const char* userPath;
|
|
const char* cachePath;
|
|
SDL_Window* window;
|
|
AuroraWindowSize windowSize;
|
|
} AuroraInfo;
|
|
|
|
AuroraInfo aurora_initialize(int argc, char* argv[], const AuroraConfig* config);
|
|
void aurora_shutdown();
|
|
const AuroraEvent* aurora_update();
|
|
bool aurora_begin_frame();
|
|
void aurora_end_frame();
|
|
// Seal the current frame with an opaque application safety tag. Aurora rejects
|
|
// an immersive provider packet unless its contentTag matches this exact frame.
|
|
void aurora_end_frame_tagged(uint64_t contentTag);
|
|
// aurora_end_frame_tagged() plus the host-owned ImGui frame to present with it (the handle from
|
|
// aurora_imgui_host_frame_end(), which this call consumes; NULL presents no host ImGui frame).
|
|
void aurora_end_frame_ex(uint64_t contentTag, void* imguiFrame);
|
|
// When the host pumps SDL events itself (aurora_update() on the window's thread) and drives
|
|
// begin/end frame from another thread, this stops those calls from pumping events.
|
|
void aurora_set_host_event_pump(bool hostPumps);
|
|
typedef void (*AuroraFrameLogCallback)(char* buffer, uint32_t bufferSize, double windowSeconds,
|
|
uint32_t frames);
|
|
// Called with each five-second frame-rate log window (where that log is enabled); a non-empty
|
|
// buffer is logged as one extra line.
|
|
void aurora_set_frame_log_callback(AuroraFrameLogCallback callback);
|
|
/**
|
|
* Relocates the immersive camera for the frame about to be sealed.
|
|
*
|
|
* anchorFromScene is a row-major affine 3x4 transform from the game's recorded
|
|
* view space into the view space the headset should render from, in world
|
|
* units. Identity keeps the recorded camera, which is the default and the
|
|
* behaviour of every frame that does not call this.
|
|
*
|
|
* Perspective draws carry the recorded camera in their own position matrices,
|
|
* so they are replayed through viewFromCenter * anchorFromScene. The 2D virtual
|
|
* screen is defined in the relocated camera's space and keeps viewFromCenter.
|
|
*
|
|
* This is latched by the next aurora_end_frame*(), then cleared: the anchor
|
|
* belongs to the guest frame that produced the GX content, so it must be
|
|
* published per frame from the producer thread rather than by the stereo
|
|
* provider, which cannot know which frame will consume its packet.
|
|
*/
|
|
void aurora_set_stereo_scene_anchor(const float anchorFromScene[12]);
|
|
// As above, also naming the world units per metre the anchor was built with.
|
|
// The sealed frame then owns that scale: each eye's head/IPD translation is
|
|
// rescaled from the packet's AuroraCockpit::unitsPerMeter to it.
|
|
void aurora_set_stereo_scene_anchor_scaled(const float anchorFromScene[12], float unitsPerMeter);
|
|
|
|
// Recorded world-to-view camera, copied on the GX thread beside the scene anchor.
|
|
// Null disables camera-separated interpolation for this frame.
|
|
void aurora_set_stereo_scene_view(const float viewFromWorld[12]);
|
|
// GX producer thread, after the scene anchor and before sealing that frame.
|
|
void aurora_set_stereo_cockpit_item(const AuroraCockpitItem* item);
|
|
// Copies a user-supplied Race/Common.szs archive. May be called on the guest
|
|
// thread; the renderer owns decoded assets and never refers back to guest RAM.
|
|
void aurora_set_cockpit_item_archive(const void* bytes, uint32_t size);
|
|
// Select Player 1's subview for immersive replay of 2-4 local screens.
|
|
// Producer-thread, per-frame metadata, consumed by the next end_frame call.
|
|
// One (the default) keeps full-frame replay. Desktop rendering is unaffected.
|
|
void aurora_set_stereo_local_player_count(uint32_t count);
|
|
/**
|
|
* VR native steering wheel: replacement position arrays for the local vehicle.
|
|
*
|
|
* GX (command processor) thread only, in order with the frame's draws: a host
|
|
* that runs GX on its own thread must post these there. `source` is the host
|
|
* pointer the game binds with GXSetArray; `replacement` is copied (at most 64
|
|
* KiB) and applies solely to draws that bind that array with a position matrix
|
|
* equal to `modelView` (row-major 3x4), so shared opponent models stay intact.
|
|
* Clearing reports how many draws the previous set matched.
|
|
*/
|
|
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.
|
|
void aurora_set_frame_worker_wait_callback(AuroraFrameWorkerWaitCallback callback);
|
|
// Registering nullptr restores the ordinary mono-only render path. Replace or
|
|
// unregister a provider only while Aurora's frame worker is idle.
|
|
void aurora_set_stereo_frame_provider(AuroraStereoFrameProvider provider, void* userdata);
|
|
void aurora_wait_for_frame_worker();
|
|
bool aurora_wait_for_frame_worker_for(uint32_t timeoutMicros);
|
|
// Producer-thread shutdown barrier. If an asynchronous cycle is waiting for
|
|
// the next begin-frame permission, grant that permission and wait until the
|
|
// worker is fully done. Unlike aurora_wait_for_frame_worker(), this is safe in
|
|
// the gap between aurora_end_frame() and aurora_begin_frame().
|
|
void aurora_quiesce_frame_worker();
|
|
// Persists the renderer's pipeline caches now (Dawn's Vulkan pipeline cache, then the queued
|
|
// pipeline recipes) and returns once they are on disk. Dawn holds the GPU device while it
|
|
// serializes its cache, so a race would see that part as a stall: call it at a race exit, when
|
|
// the session loses focus, or before ending the process.
|
|
void aurora_store_pipeline_caches();
|
|
// The same store on a background thread, returning at once, for a caller that must not wait
|
|
// (the XR pacing thread at a race exit). The device is still held while Dawn serializes.
|
|
void aurora_request_pipeline_cache_store();
|
|
// Allows the pipeline compiler to store the caches itself, rate-limited, whenever a first-use
|
|
// burst completes. Off by default; enable it while a stall is acceptable, such as in menus.
|
|
void aurora_set_pipeline_cache_idle_store(bool allowed);
|
|
// The Linux thread id of Aurora's frame worker, or 0 before it runs and where there is none.
|
|
// Android's OpenXR runtime takes it as a scheduling hint (XR_KHR_android_thread_settings).
|
|
uint32_t aurora_get_frame_worker_native_thread_id(void);
|
|
// Absolute schedule for the next sealed frame, on steady_clock: baseNanos anchors the group and
|
|
// intervalNanos is the period, so slot k of N+1 fires at base + k*interval/(N+1). Zeros clear it.
|
|
void aurora_set_present_schedule(uint64_t baseNanos, uint64_t intervalNanos);
|
|
// Reports whether the frame about to be sealed met its display boundary. Interpolation sizes its
|
|
// slot group from this, backing off after misses. Only paced presents may report.
|
|
void aurora_report_producer_paced(bool paced);
|
|
void aurora_request_frame_capture(uint32_t frame, const char* outputPath);
|
|
bool aurora_flush_efb_copies_to_ram();
|
|
bool aurora_flush_efb_copy_to_ram(void* dest);
|
|
|
|
void aurora_set_log_level(AuroraLogLevel level);
|
|
void aurora_set_pause_on_focus_lost(bool value);
|
|
void aurora_set_background_input(bool value);
|
|
void aurora_set_display_mode(AuroraDisplayMode mode);
|
|
AuroraDisplayMode aurora_get_display_mode();
|
|
|
|
AuroraBackend aurora_get_backend();
|
|
const AuroraBackend* aurora_get_available_backends(size_t* count);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|