mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 07:00:30 +02:00
Added foveated rendering for the Quest
- Eyes render under a VK_EXT_fragment_density_map: full rate around each eye's forward direction, 2x2 then 4x4 pixel blocks towards the edges ([vr] foveation = off|low|medium|high, default off). XR_FB_foveation cannot help here: the runtime's maps only shape passes drawing into its swapchain, and the eyes reach it through a copy. - aurora-main/patches/dawn/aurora_fdm.inc: Dawn enables the extension only on request and for dynamic rendering, flags every render pipeline, and chains an immutable RG8 map into any pass whose first color attachment is a view bound to one (ABI: include/aurora/dawn_fdm_abi.h). - android/Build-QuestDawn.ps1 builds the pinned Dawn revision with those patches for arm64 (dawn-build CI flags, protobuf off) into a cached package; Build-Quest.ps1 links it (-StockDawn opts out) and AuroraDawnProvider.cmake enables the ABI from its manifest. - lib/gfx/foveation.hpp generates the maps (32 px per texel, densities 255/127/63); an eye is foveated only when single_pass_eyes draws it in one render pass. Menus never are. - Live level from the headset panel's VR tab and the launcher; the launch decides whether the device has maps. debug.wiicompiled.foveation and debug.wiicompiled.fdm for A/B. - Tests: Foveation cases in gx_fifo_tests, mkw_vr_config_tests. Docs: OPENXR.md, quest-port.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ceeeba0332
commit
cc653272c8
30 files changed
+1527
-14
No files matched your search
@@ -3,6 +3,7 @@
|
||||
#ifdef AURORA_ENABLE_GX
|
||||
#include "gfx/common.hpp"
|
||||
#include "gfx/efb_ram_copy.hpp"
|
||||
#include "gfx/foveation.hpp"
|
||||
#include "gfx/stereo_replay.hpp"
|
||||
#include "gx/fifo.hpp"
|
||||
#include "gx/shader_info.hpp"
|
||||
@@ -11,6 +12,7 @@
|
||||
#include "stereo_mirror.hpp"
|
||||
#include "stereo_interpolation.hpp"
|
||||
#include "stereo_overlay.hpp"
|
||||
#include "webgpu/fdm.hpp"
|
||||
#include "webgpu/gpu.hpp"
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
#endif
|
||||
@@ -612,12 +614,27 @@ struct StereoEyeTarget {
|
||||
// Built on demand for the desktop mirror only, and dropped with the rest of
|
||||
// the target when ensure_stereo_eye_target replaces the textures.
|
||||
wgpu::BindGroup copyBindGroup;
|
||||
// Foveated rendering: a second view of `color` for the immersive eye passes,
|
||||
// which the patched Dawn binds to this eye's fragment density map
|
||||
// (webgpu/fdm.hpp), and what that map was built for.
|
||||
wgpu::TextureView foveatedView;
|
||||
uint64_t densityMap = 0;
|
||||
std::array<int32_t, 7> densityKey{};
|
||||
|
||||
const webgpu::TextureWithSampler& output() const noexcept { return resolvedColor.texture ? resolvedColor : color; }
|
||||
};
|
||||
std::array<StereoEyeTarget, AURORA_STEREO_EYE_COUNT> g_stereoEyeTargets;
|
||||
stereo::MirrorState g_stereoMirrorState;
|
||||
|
||||
// The map's binding holds the foveated view, and with it the eye texture, until it is released.
|
||||
void release_eye_density_map(StereoEyeTarget& target) noexcept {
|
||||
if (target.densityMap != 0) {
|
||||
webgpu::fdm::release_map(target.densityMap);
|
||||
target.densityMap = 0;
|
||||
}
|
||||
target.densityKey = {};
|
||||
}
|
||||
|
||||
// The eye targets outlive a frame, so the mirror samples them through a bind
|
||||
// group cached beside them rather than one built per presentation slot.
|
||||
wgpu::BindGroup stereo_eye_copy_bind_group(uint32_t eyeIndex) {
|
||||
@@ -637,6 +654,7 @@ void ensure_stereo_eye_target(uint32_t eyeIndex, uint32_t width, uint32_t height
|
||||
return;
|
||||
}
|
||||
|
||||
release_eye_density_map(target);
|
||||
target = {};
|
||||
target.color = webgpu::create_render_texture(width, height, samples > 1);
|
||||
if (samples > 1) {
|
||||
@@ -665,6 +683,56 @@ void ensure_stereo_eye_target(uint32_t eyeIndex, uint32_t width, uint32_t height
|
||||
target.depthFormat = target.depth.format;
|
||||
}
|
||||
|
||||
// The view an immersive eye's passes render through while foveated, or none. The eye's fragment
|
||||
// density map is rebuilt whenever its size, field of view or level changes (a map is immutable), and
|
||||
// is used once its upload has completed.
|
||||
wgpu::TextureView foveated_eye_view(uint32_t eyeIndex, const AuroraStereoEye& input) {
|
||||
auto& target = g_stereoEyeTargets[eyeIndex];
|
||||
const auto level = static_cast<gfx::foveation::Level>(gfx::get_stereo_foveation());
|
||||
if (level == gfx::foveation::Level::Off || target.samples > 1 || !webgpu::fdm::available()) {
|
||||
return {};
|
||||
}
|
||||
const auto fov = gfx::foveation::fov_from_projection(input.projection);
|
||||
// Hundredths of a tangent: finer than a map texel, coarse enough to ignore pose noise.
|
||||
const auto hundredths = [](float value) { return static_cast<int32_t>(std::lround(value * 100.0f)); };
|
||||
const std::array<int32_t, 7> key{static_cast<int32_t>(target.color.size.width),
|
||||
static_cast<int32_t>(target.color.size.height),
|
||||
static_cast<int32_t>(level),
|
||||
hundredths(fov.tanLeft),
|
||||
hundredths(fov.tanRight),
|
||||
hundredths(fov.tanDown),
|
||||
hundredths(fov.tanUp)};
|
||||
if (key != target.densityKey) {
|
||||
release_eye_density_map(target);
|
||||
target.densityKey = key;
|
||||
if (!target.foveatedView) {
|
||||
const wgpu::TextureViewDescriptor descriptor{
|
||||
.label = eyeIndex == 0 ? "Foveated left eye" : "Foveated right eye",
|
||||
.usage = wgpu::TextureUsage::RenderAttachment,
|
||||
};
|
||||
target.foveatedView = target.color.texture.CreateView(&descriptor);
|
||||
}
|
||||
gfx::foveation::Map map;
|
||||
gfx::foveation::build(target.color.size.width, target.color.size.height, webgpu::fdm::texel_size(), fov, level,
|
||||
map);
|
||||
target.densityMap = webgpu::fdm::create_map(map.width, map.height, map.rg8.data());
|
||||
if (target.densityMap != 0 && !webgpu::fdm::bind(target.foveatedView, target.densityMap)) {
|
||||
webgpu::fdm::release_map(target.densityMap);
|
||||
target.densityMap = 0;
|
||||
}
|
||||
static constexpr std::array<const char*, gfx::foveation::kLevelCount> kLevelNames{"off", "low", "medium", "high"};
|
||||
if (target.densityMap != 0) {
|
||||
Log.info("{} eye foveation {}: {}x{} density map, {} pixels per texel", eyeIndex == 0 ? "Left" : "Right",
|
||||
kLevelNames[static_cast<uint32_t>(level)], map.width, map.height, webgpu::fdm::texel_size());
|
||||
} else {
|
||||
Log.warn("{} eye foveation {}: the {}x{} density map could not be created", eyeIndex == 0 ? "Left" : "Right",
|
||||
kLevelNames[static_cast<uint32_t>(level)], map.width, map.height);
|
||||
}
|
||||
}
|
||||
return target.densityMap != 0 && webgpu::fdm::map_ready(target.densityMap) ? target.foveatedView
|
||||
: wgpu::TextureView{};
|
||||
}
|
||||
|
||||
std::optional<AuroraStereoFrame> request_stereo_frame(uint32_t logicalFrame, uint64_t contentTag) noexcept {
|
||||
StereoProviderRegistration registration;
|
||||
{
|
||||
@@ -772,6 +840,9 @@ gfx::StereoReplayFrame make_stereo_replay_frame(const AuroraStereoFrame& input,
|
||||
.msaaSamples = webgpu::g_graphicsConfig.msaaSamples,
|
||||
.depthFormat = owned.depth.format,
|
||||
};
|
||||
if (input.mode == AURORA_STEREO_FRAME_IMMERSIVE_REPLAY) {
|
||||
view.target.foveatedColorView = foveated_eye_view(eye, input.eyes[eye]);
|
||||
}
|
||||
std::memcpy(&view.projection, input.eyes[eye].projection, sizeof(view.projection));
|
||||
std::memcpy(&view.viewFromCenter, input.eyes[eye].viewFromCenter, sizeof(view.viewFromCenter));
|
||||
if (unitRatio != 1.f) {
|
||||
@@ -1702,6 +1773,9 @@ void shutdown() noexcept {
|
||||
stop_frame_worker();
|
||||
#ifdef AURORA_ENABLE_GX
|
||||
stop_presenter();
|
||||
for (auto& target : g_stereoEyeTargets) {
|
||||
release_eye_density_map(target);
|
||||
}
|
||||
g_stereoEyeTargets = {};
|
||||
g_stereoMirrorState.Reset();
|
||||
g_presentationImagePools = {};
|
||||
@@ -2859,6 +2933,11 @@ void aurora_set_stereo_skip_copy_clears(bool enabled) { aurora::gfx::set_stereo_
|
||||
bool aurora_get_stereo_skip_copy_clears() { return aurora::gfx::get_stereo_skip_copy_clears(); }
|
||||
void aurora_set_stereo_single_pass_eyes(bool enabled) { aurora::gfx::set_stereo_single_pass_eyes(enabled); }
|
||||
bool aurora_get_stereo_single_pass_eyes() { return aurora::gfx::get_stereo_single_pass_eyes(); }
|
||||
void aurora_set_stereo_foveation(uint32_t level) {
|
||||
aurora::gfx::set_stereo_foveation(std::min(level, aurora::gfx::foveation::kLevelCount - 1));
|
||||
}
|
||||
uint32_t aurora_get_stereo_foveation() { return aurora::gfx::get_stereo_foveation(); }
|
||||
bool aurora_stereo_foveation_available() { return aurora::webgpu::fdm::available(); }
|
||||
void aurora_set_stereo_hud_screen(bool enabled, float width, float distance) {
|
||||
aurora::gfx::set_stereo_hud_screen(enabled, width, distance);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user