mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 03:00:14 +02:00
Initial OpenXR configuration
This commit is contained in:
1 parent
9a22de1079
commit
0beb7d880c
18 files changed
+1904
-3
No files matched your search
@@ -77,6 +77,43 @@ 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 a row-major, renderer-ready 4x4 projection matrix. It uses
|
||||
* the same convention as the matrix uploaded by GXSetProjection after
|
||||
* Aurora's depth-range adjustment.
|
||||
*
|
||||
* viewFromCenter is a row-major affine 3x4 transform from the game's
|
||||
* recorded 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.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
float projection[16];
|
||||
float viewFromCenter[12];
|
||||
} AuroraStereoEye;
|
||||
|
||||
/**
|
||||
* Stereo data for one sealed GX frame. frameToken is opaque to Aurora and is
|
||||
* forwarded unchanged to the internal stereo output sink.
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t frameToken;
|
||||
AuroraStereoEye eyes[AURORA_STEREO_EYE_COUNT];
|
||||
} 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);
|
||||
|
||||
typedef struct {
|
||||
const char* appName;
|
||||
const char* userPath;
|
||||
@@ -125,6 +162,15 @@ typedef struct {
|
||||
// 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;
|
||||
// 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;
|
||||
} AuroraConfig;
|
||||
|
||||
typedef struct {
|
||||
@@ -144,6 +190,9 @@ 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);
|
||||
// Absolute schedule for the next sealed frame, on steady_clock: baseNanos anchors the group and
|
||||
|
||||
@@ -281,7 +281,32 @@ void shutdown();
|
||||
bool begin_frame();
|
||||
bool resume_frame();
|
||||
void abort_frame() noexcept;
|
||||
struct ReplayTarget {
|
||||
wgpu::TextureView colorView;
|
||||
wgpu::TextureView resolveView;
|
||||
wgpu::TextureView depthView;
|
||||
wgpu::Texture copySourceTexture;
|
||||
wgpu::TextureView copySourceView;
|
||||
wgpu::TextureView copySourceDepthView;
|
||||
wgpu::Extent3D size{};
|
||||
uint32_t msaaSamples = 1;
|
||||
};
|
||||
|
||||
struct StereoReplayEye {
|
||||
ReplayTarget target;
|
||||
Mat4x4<float> projection;
|
||||
Mat3x4<float> viewFromCenter;
|
||||
};
|
||||
|
||||
struct StereoReplayFrame {
|
||||
std::array<StereoReplayEye, AURORA_STEREO_EYE_COUNT> eyes;
|
||||
};
|
||||
|
||||
void end_frame(const wgpu::CommandEncoder& cmd);
|
||||
// Prepares eye-specific uniform copies before unmapping the staging buffer.
|
||||
// Returns false without modifying the mono path when the uniform buffer has
|
||||
// insufficient room for the additional copies.
|
||||
bool end_frame(const wgpu::CommandEncoder& cmd, const StereoReplayFrame& stereoFrame);
|
||||
void end_batch(const wgpu::CommandEncoder& cmd);
|
||||
uint32_t current_frame() noexcept;
|
||||
|
||||
@@ -314,6 +339,12 @@ void seal_frame(SealedFrame& out) noexcept;
|
||||
// so this may run concurrently with the producer's FIFO drains.
|
||||
void render(SealedFrame& frame, wgpu::CommandEncoder& cmd, int32_t interpolatedFrame = -1, bool finalize = true);
|
||||
|
||||
// Replays only main-EFB passes into one Aurora-owned eye target. Native
|
||||
// offscreen/EFB-copy passes are consumed from the mono render and are not
|
||||
// mutated by stereo replay.
|
||||
void render_stereo_eye(SealedFrame& frame, wgpu::CommandEncoder& cmd,
|
||||
const StereoReplayFrame& stereoFrame, uint32_t eye, bool finalize = false);
|
||||
|
||||
// Encode the frame that is still being recorded. Only for the synchronous
|
||||
// EFB-readback split path, which runs on the producer thread.
|
||||
void render(wgpu::CommandEncoder& cmd, int32_t interpolatedFrame = -1, bool finalize = true);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <aurora/math.hpp>
|
||||
|
||||
namespace aurora::gfx::stereo_replay {
|
||||
|
||||
// Aurora stores the GX 3x4 matrices row-major. The vertex shader consumes
|
||||
// them as vec4 * mat3x4, which is equivalent to the original column-vector
|
||||
// affine transform. Applying an eye-space delta therefore composes delta *
|
||||
// objectToCenter in the ordinary row-major notation used below.
|
||||
inline Mat3x4<float> compose_affine(const Mat3x4<float>& viewFromCenter,
|
||||
const Mat3x4<float>& objectToCenter) noexcept {
|
||||
Mat3x4<float> out{};
|
||||
for (size_t row = 0; row < 3; ++row) {
|
||||
auto& dst = *(&out.m0 + row);
|
||||
const auto& view = *(&viewFromCenter.m0 + row);
|
||||
for (size_t column = 0; column < 3; ++column) {
|
||||
dst[column] = view[0] * objectToCenter.m0[column] + view[1] * objectToCenter.m1[column] +
|
||||
view[2] * objectToCenter.m2[column];
|
||||
}
|
||||
dst[3] = view[3] + view[0] * objectToCenter.m0[3] + view[1] * objectToCenter.m1[3] +
|
||||
view[2] * objectToCenter.m2[3];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Normals receive only the eye transform's linear part. OpenXR view deltas
|
||||
// are rigid transforms, so no inverse-transpose correction is needed here.
|
||||
inline Mat3x4<float> compose_normal(const Mat3x4<float>& viewFromCenter,
|
||||
const Mat3x4<float>& objectToCenter) noexcept {
|
||||
Mat3x4<float> out{};
|
||||
for (size_t row = 0; row < 3; ++row) {
|
||||
auto& dst = *(&out.m0 + row);
|
||||
const auto& view = *(&viewFromCenter.m0 + row);
|
||||
for (size_t column = 0; column < 3; ++column) {
|
||||
dst[column] = view[0] * objectToCenter.m0[column] + view[1] * objectToCenter.m1[column] +
|
||||
view[2] * objectToCenter.m2[column];
|
||||
}
|
||||
dst[3] = 0.0f;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace aurora::gfx::stereo_replay
|
||||
@@ -2256,9 +2256,9 @@ static void handle_draw_unmerged(GXPrimitive prim, GXVtxFmt fmt, u16 vtxCount,
|
||||
.matrixTopology = matrixTopologySignature,
|
||||
};
|
||||
}
|
||||
const bool perspective = g_gxState.projType == GX_PERSPECTIVE;
|
||||
const auto uniformRanges =
|
||||
build_uniform(info, vertRange.offset, ranges, drawIdentity, interpolationIdentityActive,
|
||||
usedPnMtxMask);
|
||||
build_uniform(info, vertRange.offset, ranges, drawIdentity, perspective, usedPnMtxMask);
|
||||
s_lastDrawRecordedInterpolation = interpolationIdentityActive;
|
||||
|
||||
uint32_t instanceCount = 1;
|
||||
@@ -2275,6 +2275,8 @@ static void handle_draw_unmerged(GXPrimitive prim, GXVtxFmt fmt, u16 vtxCount,
|
||||
.idxRange = idxRange,
|
||||
.uniformRange = uniformRanges.current,
|
||||
.interpolatedUniformRanges = uniformRanges.interpolated,
|
||||
.stereoUniformRanges = {},
|
||||
.uniformReplayLayout = uniformRanges.replayLayout,
|
||||
.vtxCount = vtxCount,
|
||||
.indexCount = numIndices,
|
||||
.instanceCount = instanceCount,
|
||||
|
||||
@@ -10,6 +10,8 @@ struct DrawData {
|
||||
gfx::Range idxRange;
|
||||
gfx::Range uniformRange;
|
||||
std::array<gfx::Range, MaxInterpolatedFrames> interpolatedUniformRanges;
|
||||
std::array<gfx::Range, AURORA_STEREO_EYE_COUNT> stereoUniformRanges;
|
||||
UniformReplayLayout uniformReplayLayout;
|
||||
uint32_t vtxCount;
|
||||
uint32_t indexCount;
|
||||
uint32_t instanceCount;
|
||||
|
||||
@@ -613,10 +613,14 @@ UniformRanges build_uniform(const ShaderInfo& info, u32 vtxStart, const BindGrou
|
||||
stage(&effectiveProj, sizeof(effectiveProj));
|
||||
|
||||
const size_t positionOffset = stagedSize;
|
||||
uint32_t positionMatrixMask = 0;
|
||||
for (u32 i = 0; i < layout.postexCount; ++i) {
|
||||
const u32 slot =
|
||||
layout.postexSlots[i] == UniformMatrixLayout::kCurrentPnMtx ? currentPostexSlot
|
||||
: layout.postexSlots[i];
|
||||
if (slot < MaxPnMtx) {
|
||||
positionMatrixMask |= 1u << i;
|
||||
}
|
||||
stage(slot < MaxPnMtx ? &g_gxState.pnMtx[slot].pos : &g_gxState.texMtxs[slot - MaxPnMtx],
|
||||
sizeof(Mat3x4<float>));
|
||||
}
|
||||
@@ -707,11 +711,22 @@ UniformRanges build_uniform(const ShaderInfo& info, u32 vtxStart, const BindGrou
|
||||
buf.append(texture_size_bias(tex));
|
||||
}
|
||||
|
||||
const UniformReplayLayout replayLayout{
|
||||
.projectionOffset = static_cast<uint32_t>(projectionOffset),
|
||||
.positionOffset = static_cast<uint32_t>(positionOffset),
|
||||
.normalOffset = static_cast<uint32_t>(normalOffset),
|
||||
.positionMatrixMask = positionMatrixMask,
|
||||
.positionMatrixCount = layout.postexCount,
|
||||
.normalMatrixCount = layout.nrmCount,
|
||||
.perspective = perspective,
|
||||
};
|
||||
|
||||
if (!perspective || frame_interpolation_fps() == 0) {
|
||||
g_gxState.stateDirty = false;
|
||||
return {
|
||||
.current = range,
|
||||
.interpolated = {},
|
||||
.replayLayout = replayLayout,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -733,6 +748,7 @@ UniformRanges build_uniform(const ShaderInfo& info, u32 vtxStart, const BindGrou
|
||||
return {
|
||||
.current = range,
|
||||
.interpolated = interpolatedRanges,
|
||||
.replayLayout = replayLayout,
|
||||
};
|
||||
}
|
||||
} // namespace aurora::gx
|
||||
@@ -5,9 +5,20 @@
|
||||
#include "frame_interpolation.hpp"
|
||||
|
||||
namespace aurora::gx {
|
||||
struct UniformReplayLayout {
|
||||
uint32_t projectionOffset = 0;
|
||||
uint32_t positionOffset = 0;
|
||||
uint32_t normalOffset = 0;
|
||||
uint32_t positionMatrixMask = 0;
|
||||
uint8_t positionMatrixCount = 0;
|
||||
uint8_t normalMatrixCount = 0;
|
||||
bool perspective = false;
|
||||
};
|
||||
|
||||
struct UniformRanges {
|
||||
gfx::Range current;
|
||||
std::array<gfx::Range, MaxInterpolatedFrames> interpolated;
|
||||
UniformReplayLayout replayLayout;
|
||||
};
|
||||
|
||||
ShaderInfo build_shader_info(const ShaderConfig& config) noexcept;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "webgpu/gpu.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace aurora::stereo {
|
||||
|
||||
struct EyeImage {
|
||||
// Borrowed for the duration of the sink callback. A sink may encode work
|
||||
// that reads the texture, but must not retain these pointers.
|
||||
const wgpu::Texture* texture = nullptr;
|
||||
const wgpu::TextureView* view = nullptr;
|
||||
wgpu::Extent3D size{};
|
||||
wgpu::TextureFormat format = wgpu::TextureFormat::Undefined;
|
||||
};
|
||||
|
||||
struct SinkFrame {
|
||||
uint64_t frameToken = 0;
|
||||
uint32_t logicalFrame = 0;
|
||||
std::array<EyeImage, AURORA_STEREO_EYE_COUNT> eyes{};
|
||||
};
|
||||
|
||||
// Runs synchronously on the frame worker after both eye replays have been
|
||||
// encoded and before its command buffer is submitted. Backend interop code
|
||||
// can append copies/import transitions to the same encoder here.
|
||||
using SinkCallback = void (*)(wgpu::CommandEncoder& encoder, const SinkFrame& frame, void* userdata) noexcept;
|
||||
|
||||
// Internal Aurora hook: D3D12/Vulkan OpenXR interop owns this registration.
|
||||
// Registration changes must happen while the frame worker is idle.
|
||||
void set_sink(SinkCallback callback, void* userdata) noexcept;
|
||||
|
||||
} // namespace aurora::stereo
|
||||
@@ -43,6 +43,18 @@ void clear_offscreen_cache();
|
||||
namespace aurora::webgpu {
|
||||
static Module Log("aurora::gpu");
|
||||
|
||||
#if defined(WEBGPU_DAWN) && defined(_WIN32)
|
||||
// RequestAdapterOptionsLUID is a Dawn-native extension whose exported C++
|
||||
// constructor cannot be called safely by this project's LLVM-MinGW consumer.
|
||||
// Its wire representation is deliberately just a WebGPU chained header plus
|
||||
// the Win32 LUID, so describe that C ABI directly and let RequestAdapter
|
||||
// consume it through the ordinary WebGPU entry point.
|
||||
struct RequestAdapterOptionsLuidWire {
|
||||
wgpu::ChainedStruct chain{};
|
||||
LUID adapterLuid{};
|
||||
};
|
||||
#endif
|
||||
|
||||
wgpu::Device g_device;
|
||||
wgpu::Queue g_queue;
|
||||
wgpu::Surface g_surface;
|
||||
@@ -558,11 +570,24 @@ bool initialize(AuroraBackend auroraBackend) {
|
||||
}
|
||||
}
|
||||
{
|
||||
const wgpu::RequestAdapterOptions options{
|
||||
wgpu::RequestAdapterOptions options{
|
||||
.powerPreference = wgpu::PowerPreference::HighPerformance,
|
||||
.backendType = backend,
|
||||
.compatibleSurface = g_surface,
|
||||
};
|
||||
#if defined(WEBGPU_DAWN) && defined(_WIN32)
|
||||
RequestAdapterOptionsLuidWire luidOptions{};
|
||||
if (backend == wgpu::BackendType::D3D12 && g_config.xrInterop &&
|
||||
g_config.hasD3D12AdapterLuid) {
|
||||
luidOptions.chain.sType = wgpu::SType::RequestAdapterOptionsLUID;
|
||||
luidOptions.adapterLuid.LowPart = g_config.d3d12AdapterLuidLow;
|
||||
luidOptions.adapterLuid.HighPart = g_config.d3d12AdapterLuidHigh;
|
||||
options.nextInChain = &luidOptions.chain;
|
||||
Log.info("Pinning D3D12 adapter to OpenXR LUID {:08x}:{:08x}",
|
||||
static_cast<uint32_t>(luidOptions.adapterLuid.HighPart),
|
||||
luidOptions.adapterLuid.LowPart);
|
||||
}
|
||||
#endif
|
||||
const auto future = g_instance.RequestAdapter(
|
||||
&options, wgpu::CallbackMode::WaitAnyOnly,
|
||||
[](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message) {
|
||||
@@ -653,6 +678,12 @@ bool initialize(AuroraBackend auroraBackend) {
|
||||
implicitDeviceSynchronizationSupported = true;
|
||||
requiredFeatures.push_back(feature);
|
||||
}
|
||||
#if defined(WEBGPU_DAWN) && defined(_WIN32)
|
||||
if (g_config.xrInterop && g_backendType == wgpu::BackendType::D3D12 &&
|
||||
feature == wgpu::FeatureName::SharedTextureMemoryD3D12Resource) {
|
||||
requiredFeatures.push_back(feature);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (!implicitDeviceSynchronizationSupported) {
|
||||
Log.warn(
|
||||
|
||||
Reference in new issue
Block a user