mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 06:00:25 +02:00
Implement stereo depth and stencil support for VR cockpit rendering to fix hands getting darken by virtual screen
This commit is contained in:
1 parent
b2353e1410
commit
2a89fac79d
12 files changed
+147
-61
No files matched your search
@@ -471,8 +471,13 @@ offers that mesh without the app declaring hand tracking, and the log says which
|
|||||||
joint's own X, on both hands. They and the
|
joint's own X, on both hands. They and the
|
||||||
separate VR wheel or handlebar travel with the stereo packet in metres in the seated frame, and each
|
separate VR wheel or handlebar travel with the stereo packet in metres in the seated frame, and each
|
||||||
eye draws them inside the scene's pass just before the first 2D-layer draw, depth-tested with the
|
eye draws them inside the scene's pass just before the first 2D-layer draw, depth-tested with the
|
||||||
world's own depth mapping, so the kart and the track hide them and the HUD cannot
|
world's own depth mapping, so the kart and the track hide them. Visible cockpit samples
|
||||||
(`aurora-main/lib/gfx/cockpit.hpp`). The anchor also carries the frame's exact world scale
|
also mark one stencil bit; virtual-screen draws test that bit for zero, so even depth-disabled
|
||||||
|
HUD elements and black screen effects cannot paint over the hands. Only stereo eye targets
|
||||||
|
use `Depth24PlusStencil8`; desktop/EFB depth stays unchanged. The mask is cleared once per
|
||||||
|
eye replay and retained across its passes. This adds no draw, full-screen copy, or render pass;
|
||||||
|
eye-format pipeline siblings share shader modules and are cached when recording the game
|
||||||
|
frame (`aurora-main/lib/gfx/cockpit.hpp`). The anchor also carries the frame's exact world scale
|
||||||
(`aurora_set_stereo_scene_anchor_scaled`), and Aurora rescales each eye's head translation to it, so
|
(`aurora_set_stereo_scene_anchor_scaled`), and Aurora rescales each eye's head translation to it, so
|
||||||
a scale change between the XR packet and the frame cannot misplace the hands.
|
a scale change between the XR packet and the frame cannot misplace the hands.
|
||||||
|
|
||||||
|
|||||||
@@ -633,7 +633,7 @@ void ensure_stereo_eye_target(uint32_t eyeIndex, uint32_t width, uint32_t height
|
|||||||
const uint32_t samples = webgpu::g_graphicsConfig.msaaSamples;
|
const uint32_t samples = webgpu::g_graphicsConfig.msaaSamples;
|
||||||
if (target.color.texture && target.requestedWidth == width && target.requestedHeight == height &&
|
if (target.color.texture && target.requestedWidth == width && target.requestedHeight == height &&
|
||||||
target.samples == samples && target.colorFormat == webgpu::g_graphicsConfig.surfaceConfiguration.format &&
|
target.samples == samples && target.colorFormat == webgpu::g_graphicsConfig.surfaceConfiguration.format &&
|
||||||
target.depthFormat == webgpu::g_graphicsConfig.depthFormat) {
|
target.depthFormat == wgpu::TextureFormat::Depth24PlusStencil8) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -643,19 +643,21 @@ void ensure_stereo_eye_target(uint32_t eyeIndex, uint32_t width, uint32_t height
|
|||||||
target.resolvedColor = webgpu::create_render_texture(target.color.size.width, target.color.size.height, false);
|
target.resolvedColor = webgpu::create_render_texture(target.color.size.width, target.color.size.height, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The cockpit uses one stencil bit to survive later depth-disabled HUD draws.
|
||||||
|
// Keep this attachment eye-only; native EFB depth sampling is unchanged.
|
||||||
const wgpu::TextureDescriptor depthDescriptor{
|
const wgpu::TextureDescriptor depthDescriptor{
|
||||||
.label = eyeIndex == 0 ? "Stereo left eye depth" : "Stereo right eye depth",
|
.label = eyeIndex == 0 ? "Stereo left eye depth" : "Stereo right eye depth",
|
||||||
.usage = wgpu::TextureUsage::RenderAttachment,
|
.usage = wgpu::TextureUsage::RenderAttachment,
|
||||||
.dimension = wgpu::TextureDimension::e2D,
|
.dimension = wgpu::TextureDimension::e2D,
|
||||||
.size = target.color.size,
|
.size = target.color.size,
|
||||||
.format = webgpu::g_graphicsConfig.depthFormat,
|
.format = wgpu::TextureFormat::Depth24PlusStencil8,
|
||||||
.mipLevelCount = 1,
|
.mipLevelCount = 1,
|
||||||
.sampleCount = samples,
|
.sampleCount = samples,
|
||||||
};
|
};
|
||||||
target.depth.texture = g_device.CreateTexture(&depthDescriptor);
|
target.depth.texture = g_device.CreateTexture(&depthDescriptor);
|
||||||
target.depth.view = target.depth.texture.CreateView();
|
target.depth.view = target.depth.texture.CreateView();
|
||||||
target.depth.size = target.color.size;
|
target.depth.size = target.color.size;
|
||||||
target.depth.format = webgpu::g_graphicsConfig.depthFormat;
|
target.depth.format = wgpu::TextureFormat::Depth24PlusStencil8;
|
||||||
target.requestedWidth = width;
|
target.requestedWidth = width;
|
||||||
target.requestedHeight = height;
|
target.requestedHeight = height;
|
||||||
target.samples = samples;
|
target.samples = samples;
|
||||||
@@ -768,6 +770,7 @@ gfx::StereoReplayFrame make_stereo_replay_frame(const AuroraStereoFrame& input,
|
|||||||
.copySourceDepthView = owned.depth.view,
|
.copySourceDepthView = owned.depth.view,
|
||||||
.size = owned.color.size,
|
.size = owned.color.size,
|
||||||
.msaaSamples = webgpu::g_graphicsConfig.msaaSamples,
|
.msaaSamples = webgpu::g_graphicsConfig.msaaSamples,
|
||||||
|
.depthFormat = owned.depth.format,
|
||||||
};
|
};
|
||||||
std::memcpy(&view.projection, input.eyes[eye].projection, sizeof(view.projection));
|
std::memcpy(&view.projection, input.eyes[eye].projection, sizeof(view.projection));
|
||||||
std::memcpy(&view.viewFromCenter, input.eyes[eye].viewFromCenter, sizeof(view.viewFromCenter));
|
std::memcpy(&view.viewFromCenter, input.eyes[eye].viewFromCenter, sizeof(view.viewFromCenter));
|
||||||
|
|||||||
@@ -505,6 +505,13 @@ void GXCopyTex(void* dest, GXBool clear) {
|
|||||||
.clearAlpha = true,
|
.clearAlpha = true,
|
||||||
.clearDepth = false,
|
.clearDepth = false,
|
||||||
}),
|
}),
|
||||||
|
.stereoPipeline = aurora::stereo_frame_provider_active() ? aurora::gfx::pipeline_ref(aurora::gfx::clear::PipelineConfig{
|
||||||
|
.msaaSamples = aurora::gfx::get_sample_count(),
|
||||||
|
.clearColor = false,
|
||||||
|
.clearAlpha = true,
|
||||||
|
.clearDepth = false,
|
||||||
|
.stereoStencil = true,
|
||||||
|
}) : 0,
|
||||||
.color = wgpu::Color{0.f, 0.f, 0.f, g_gxState.dstAlpha / 255.f},
|
.color = wgpu::Color{0.f, 0.f, 0.f, g_gxState.dstAlpha / 255.f},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ fn fs_main() -> FragmentOutput {
|
|||||||
.targets = &colorTarget,
|
.targets = &colorTarget,
|
||||||
};
|
};
|
||||||
const wgpu::DepthStencilState depthStencil{
|
const wgpu::DepthStencilState depthStencil{
|
||||||
.format = g_graphicsConfig.depthFormat,
|
.format = config.stereoStencil ? wgpu::TextureFormat::Depth24PlusStencil8 : g_graphicsConfig.depthFormat,
|
||||||
.depthWriteEnabled = config.clearDepth,
|
.depthWriteEnabled = config.clearDepth,
|
||||||
.depthCompare = wgpu::CompareFunction::Always,
|
.depthCompare = wgpu::CompareFunction::Always,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
namespace aurora::gfx::clear {
|
namespace aurora::gfx::clear {
|
||||||
struct DrawData {
|
struct DrawData {
|
||||||
PipelineRef pipeline;
|
PipelineRef pipeline;
|
||||||
|
PipelineRef stereoPipeline = 0;
|
||||||
Range uniformRange;
|
Range uniformRange;
|
||||||
wgpu::Color color;
|
wgpu::Color color;
|
||||||
float depth = 0.f;
|
float depth = 0.f;
|
||||||
@@ -18,14 +19,14 @@ struct DrawData {
|
|||||||
ClipRect scissor{};
|
ClipRect scissor{};
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint32_t ClearPipelineConfigVersion = 3;
|
constexpr uint32_t ClearPipelineConfigVersion = 4;
|
||||||
struct PipelineConfig {
|
struct PipelineConfig {
|
||||||
uint32_t version = ClearPipelineConfigVersion;
|
uint32_t version = ClearPipelineConfigVersion;
|
||||||
uint32_t msaaSamples = 1;
|
uint32_t msaaSamples = 1;
|
||||||
bool clearColor = true;
|
bool clearColor = true;
|
||||||
bool clearAlpha = true;
|
bool clearAlpha = true;
|
||||||
bool clearDepth = true;
|
bool clearDepth = true;
|
||||||
uint8_t _pad = 0;
|
bool stereoStencil = false;
|
||||||
};
|
};
|
||||||
static_assert(std::has_unique_object_representations_v<PipelineConfig>);
|
static_assert(std::has_unique_object_representations_v<PipelineConfig>);
|
||||||
|
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ struct SceneDepth {
|
|||||||
};
|
};
|
||||||
inline uint32_t pipelineSamples=0;
|
inline uint32_t pipelineSamples=0;
|
||||||
inline bool pipelineReversedDepth=false;
|
inline bool pipelineReversedDepth=false;
|
||||||
inline wgpu::TextureFormat pipelineFormat{};
|
inline wgpu::TextureFormat pipelineFormat{}, pipelineDepthFormat{};
|
||||||
inline std::array<wgpu::Buffer,2> vertexBuffers;
|
inline std::array<wgpu::Buffer,2> vertexBuffers;
|
||||||
inline std::array<uint64_t,2> vertexCapacity{};
|
inline std::array<uint64_t,2> vertexCapacity{};
|
||||||
inline void shutdown() { pipeline=nullptr;pipelineSamples=0;vertexBuffers={};vertexCapacity={};cachedMeshRevision=0;frameVertices.clear(); }
|
inline void shutdown() { pipeline=nullptr;pipelineSamples=0;vertexBuffers={};vertexCapacity={};cachedMeshRevision=0;frameVertices.clear(); }
|
||||||
@@ -223,7 +223,7 @@ inline void render(wgpu::CommandEncoder& cmd,const StereoReplayFrame& frame,uint
|
|||||||
// The guest can reverse its viewport depth independently of Aurora's
|
// The guest can reverse its viewport depth independently of Aurora's
|
||||||
// global reversed-Z convention. The final 1/d coefficient is authoritative.
|
// global reversed-Z convention. The final 1/d coefficient is authoritative.
|
||||||
const bool reversedDepth=sceneDepth.constant>0;
|
const bool reversedDepth=sceneDepth.constant>0;
|
||||||
if(!pipeline||pipelineSamples!=target.msaaSamples||pipelineFormat!=format||pipelineReversedDepth!=reversedDepth) {
|
if(!pipeline||pipelineSamples!=target.msaaSamples||pipelineFormat!=format||pipelineReversedDepth!=reversedDepth||pipelineDepthFormat!=target.depthFormat) {
|
||||||
wgpu::ShaderSourceWGSL source{};
|
wgpu::ShaderSourceWGSL source{};
|
||||||
source.code=R"(
|
source.code=R"(
|
||||||
struct Out { @builtin(position) position: vec4f, @location(0) color: vec3f };
|
struct Out { @builtin(position) position: vec4f, @location(0) color: vec3f };
|
||||||
@@ -239,14 +239,18 @@ inline void render(wgpu::CommandEncoder& cmd,const StereoReplayFrame& frame,uint
|
|||||||
const wgpu::VertexBufferLayout layout{.arrayStride=28,.attributeCount=2,.attributes=attrs};
|
const wgpu::VertexBufferLayout layout{.arrayStride=28,.attributeCount=2,.attributes=attrs};
|
||||||
const wgpu::ColorTargetState color{.format=format};
|
const wgpu::ColorTargetState color{.format=format};
|
||||||
const wgpu::FragmentState fragment{.module=shader,.entryPoint="fs",.targetCount=1,.targets=&color};
|
const wgpu::FragmentState fragment{.module=shader,.entryPoint="fs",.targetCount=1,.targets=&color};
|
||||||
const wgpu::DepthStencilState depth{.format=g_graphicsConfig.depthFormat,.depthWriteEnabled=true,
|
const bool stencil=target.depthFormat==wgpu::TextureFormat::Depth24PlusStencil8;
|
||||||
.depthCompare=reversedDepth?wgpu::CompareFunction::GreaterEqual:wgpu::CompareFunction::LessEqual};
|
const wgpu::StencilFaceState mark{.compare=wgpu::CompareFunction::Always,
|
||||||
|
.passOp=stencil?wgpu::StencilOperation::Replace:wgpu::StencilOperation::Keep};
|
||||||
|
const wgpu::DepthStencilState depth{.format=target.depthFormat,.depthWriteEnabled=true,
|
||||||
|
.depthCompare=reversedDepth?wgpu::CompareFunction::GreaterEqual:wgpu::CompareFunction::LessEqual,
|
||||||
|
.stencilFront=mark,.stencilBack=mark,.stencilReadMask=1,.stencilWriteMask=stencil?1u:0u};
|
||||||
wgpu::RenderPipelineDescriptor desc{};desc.label="VR cockpit";
|
wgpu::RenderPipelineDescriptor desc{};desc.label="VR cockpit";
|
||||||
desc.vertex={.module=shader,.entryPoint="vs",.bufferCount=1,.buffers=&layout};
|
desc.vertex={.module=shader,.entryPoint="vs",.bufferCount=1,.buffers=&layout};
|
||||||
desc.fragment=&fragment;desc.depthStencil=&depth;desc.multisample.count=target.msaaSamples;
|
desc.fragment=&fragment;desc.depthStencil=&depth;desc.multisample.count=target.msaaSamples;
|
||||||
desc.primitive.topology=wgpu::PrimitiveTopology::TriangleList;
|
desc.primitive.topology=wgpu::PrimitiveTopology::TriangleList;
|
||||||
pipeline=g_device.CreateRenderPipeline(&desc);pipelineSamples=target.msaaSamples;pipelineFormat=format;
|
pipeline=g_device.CreateRenderPipeline(&desc);pipelineSamples=target.msaaSamples;pipelineFormat=format;
|
||||||
pipelineReversedDepth=reversedDepth;
|
pipelineReversedDepth=reversedDepth;pipelineDepthFormat=target.depthFormat;
|
||||||
}
|
}
|
||||||
const auto revision=meshRevision.load();
|
const auto revision=meshRevision.load();
|
||||||
if(cachedMeshRevision!=revision || std::memcmp(&cachedCockpit,&frame.cockpit,sizeof(AuroraCockpit))!=0) {
|
if(cachedMeshRevision!=revision || std::memcmp(&cachedCockpit,&frame.cockpit,sizeof(AuroraCockpit))!=0) {
|
||||||
@@ -279,12 +283,17 @@ inline void render(wgpu::CommandEncoder& cmd,const StereoReplayFrame& frame,uint
|
|||||||
const wgpu::RenderPassColorAttachment attachment{.view=target.colorView,.resolveTarget=target.resolveView,
|
const wgpu::RenderPassColorAttachment attachment{.view=target.colorView,.resolveTarget=target.resolveView,
|
||||||
.loadOp=wgpu::LoadOp::Load,.storeOp=wgpu::StoreOp::Store};
|
.loadOp=wgpu::LoadOp::Load,.storeOp=wgpu::StoreOp::Store};
|
||||||
const wgpu::RenderPassDepthStencilAttachment depth{.view=target.depthView,.depthLoadOp=wgpu::LoadOp::Load,
|
const wgpu::RenderPassDepthStencilAttachment depth{.view=target.depthView,.depthLoadOp=wgpu::LoadOp::Load,
|
||||||
.depthStoreOp=wgpu::StoreOp::Store,.depthClearValue=1.0f};
|
.depthStoreOp=wgpu::StoreOp::Store,.depthClearValue=1.0f,
|
||||||
|
.stencilLoadOp=target.depthFormat==wgpu::TextureFormat::Depth24PlusStencil8?wgpu::LoadOp::Load:wgpu::LoadOp::Undefined,
|
||||||
|
.stencilStoreOp=target.depthFormat==wgpu::TextureFormat::Depth24PlusStencil8?wgpu::StoreOp::Store:wgpu::StoreOp::Undefined};
|
||||||
const wgpu::RenderPassDescriptor pd{.label="VR cockpit overlay",.colorAttachmentCount=1,.colorAttachments=&attachment,.depthStencilAttachment=&depth};
|
const wgpu::RenderPassDescriptor pd{.label="VR cockpit overlay",.colorAttachmentCount=1,.colorAttachments=&attachment,.depthStencilAttachment=&depth};
|
||||||
auto pass=existingPass?*existingPass:cmd.BeginRenderPass(&pd);
|
auto pass=existingPass?*existingPass:cmd.BeginRenderPass(&pd);
|
||||||
pass.SetViewport(0,0,float(target.size.width),float(target.size.height),0,1);
|
pass.SetViewport(0,0,float(target.size.width),float(target.size.height),0,1);
|
||||||
pass.SetScissorRect(0,0,target.size.width,target.size.height);
|
pass.SetScissorRect(0,0,target.size.width,target.size.height);
|
||||||
|
// Mark only depth-visible samples; later virtual-screen draws test for zero.
|
||||||
|
pass.SetStencilReference(1);
|
||||||
pass.SetPipeline(pipeline);pass.SetVertexBuffer(0,buffer);pass.Draw(clip.size());
|
pass.SetPipeline(pipeline);pass.SetVertexBuffer(0,buffer);pass.Draw(clip.size());
|
||||||
|
pass.SetStencilReference(0);
|
||||||
if(!existingPass) pass.End();
|
if(!existingPass) pass.End();
|
||||||
}
|
}
|
||||||
} // namespace aurora::gfx::cockpit
|
} // namespace aurora::gfx::cockpit
|
||||||
@@ -737,6 +737,13 @@ void resolve_pass(TextureHandle texture, ClipRect rect, bool clearColor, bool cl
|
|||||||
.clearAlpha = clearAlpha,
|
.clearAlpha = clearAlpha,
|
||||||
.clearDepth = clearDepth,
|
.clearDepth = clearDepth,
|
||||||
}),
|
}),
|
||||||
|
.stereoPipeline = aurora::stereo_frame_provider_active() ? pipeline_ref(clear::PipelineConfig{
|
||||||
|
.msaaSamples = msaaSamples,
|
||||||
|
.clearColor = clearColor,
|
||||||
|
.clearAlpha = clearAlpha,
|
||||||
|
.clearDepth = clearDepth,
|
||||||
|
.stereoStencil = true,
|
||||||
|
}) : 0,
|
||||||
.color =
|
.color =
|
||||||
wgpu::Color{
|
wgpu::Color{
|
||||||
.r = clearColorValue.x(),
|
.r = clearColorValue.x(),
|
||||||
@@ -1769,6 +1776,9 @@ static void render_impl(std::vector<RenderPass>& renderPasses, wgpu::CommandEnco
|
|||||||
ZoneScoped;
|
ZoneScoped;
|
||||||
// Palette conversions, MSAA resolves and EFB copies depend on sealed frame state, not on the
|
// Palette conversions, MSAA resolves and EFB copies depend on sealed frame state, not on the
|
||||||
// interpolation weight, so encode them on the native render and let replay slots sample them.
|
// interpolation weight, so encode them on the native render and let replay slots sample them.
|
||||||
|
// Eye textures are reused; discard the previous frame's mask, then retain it
|
||||||
|
// across guest passes even if the HUD clears or replaces guest depth.
|
||||||
|
bool stencilInitialized = false;
|
||||||
for (u32 i = 0; i < renderPasses.size(); ++i) {
|
for (u32 i = 0; i < renderPasses.size(); ++i) {
|
||||||
const auto& passInfo = renderPasses[i];
|
const auto& passInfo = renderPasses[i];
|
||||||
if (invocation.replayLastPass >= 0 && i > static_cast<u32>(invocation.replayLastPass)) {
|
if (invocation.replayLastPass >= 0 && i > static_cast<u32>(invocation.replayLastPass)) {
|
||||||
@@ -1821,11 +1831,16 @@ static void render_impl(std::vector<RenderPass>& renderPasses, wgpu::CommandEnco
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const bool stereoStencil = overrideTarget &&
|
||||||
|
invocation.target->depthFormat == wgpu::TextureFormat::Depth24PlusStencil8;
|
||||||
const wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{
|
const wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{
|
||||||
.view = depthView,
|
.view = depthView,
|
||||||
.depthLoadOp = passInfo.clearDepth && !dropCopyClear ? wgpu::LoadOp::Clear : wgpu::LoadOp::Load,
|
.depthLoadOp = passInfo.clearDepth && !dropCopyClear ? wgpu::LoadOp::Clear : wgpu::LoadOp::Load,
|
||||||
.depthStoreOp = wgpu::StoreOp::Store,
|
.depthStoreOp = wgpu::StoreOp::Store,
|
||||||
.depthClearValue = passInfo.clearDepthValue,
|
.depthClearValue = passInfo.clearDepthValue,
|
||||||
|
.stencilLoadOp = stereoStencil ? (stencilInitialized ? wgpu::LoadOp::Load : wgpu::LoadOp::Clear) : wgpu::LoadOp::Undefined,
|
||||||
|
.stencilStoreOp = stereoStencil ? wgpu::StoreOp::Store : wgpu::StoreOp::Undefined,
|
||||||
|
.stencilClearValue = 0,
|
||||||
};
|
};
|
||||||
const GpuTimingCategory timingCategory = invocation.stereoEye == 0 ? GpuTimingCategory::EyeLeft
|
const GpuTimingCategory timingCategory = invocation.stereoEye == 0 ? GpuTimingCategory::EyeLeft
|
||||||
: invocation.stereoEye == 1 ? GpuTimingCategory::EyeRight
|
: invocation.stereoEye == 1 ? GpuTimingCategory::EyeRight
|
||||||
@@ -1839,6 +1854,7 @@ static void render_impl(std::vector<RenderPass>& renderPasses, wgpu::CommandEnco
|
|||||||
.timestampWrites = gpu_timing_pass(timingCategory),
|
.timestampWrites = gpu_timing_pass(timingCategory),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (stereoStencil) stencilInitialized = true;
|
||||||
auto pass = cmd.BeginRenderPass(&renderPassDescriptor);
|
auto pass = cmd.BeginRenderPass(&renderPassDescriptor);
|
||||||
render_pass_impl(pass, renderPasses, i, invocation);
|
render_pass_impl(pass, renderPasses, i, invocation);
|
||||||
pass.End();
|
pass.End();
|
||||||
@@ -2500,8 +2516,8 @@ static void render_pass_impl(const wgpu::RenderPassEncoder& pass, const std::vec
|
|||||||
draw.gx.interpolatedUniformRanges[invocation.interpolatedFrame].size != 0) {
|
draw.gx.interpolatedUniformRanges[invocation.interpolatedFrame].size != 0) {
|
||||||
uniformOverride = &draw.gx.interpolatedUniformRanges[invocation.interpolatedFrame];
|
uniformOverride = &draw.gx.interpolatedUniformRanges[invocation.interpolatedFrame];
|
||||||
}
|
}
|
||||||
// Draw the VR cockpit against the world's depth before the first HUD
|
// Draw against world depth and mark visible cockpit samples before HUD
|
||||||
// draw can write a screen-plane depth over it, inside this open pass.
|
// depth replaces it. The screen pipelines reject those stencil samples.
|
||||||
if (invocation.cockpitFrame != nullptr && overrideTarget) {
|
if (invocation.cockpitFrame != nullptr && overrideTarget) {
|
||||||
if (draw.gx.uniformReplayLayout.perspective) {
|
if (draw.gx.uniformReplayLayout.perspective) {
|
||||||
*invocation.sceneDrawn = true;
|
*invocation.sceneDrawn = true;
|
||||||
@@ -2535,10 +2551,15 @@ static void render_pass_impl(const wgpu::RenderPassEncoder& pass, const std::vec
|
|||||||
apply_viewport(fullEyeDraw);
|
apply_viewport(fullEyeDraw);
|
||||||
}
|
}
|
||||||
gx::render(draw.gx, pass, encodeState, renderPasses[idx].requireReadyPipelines, uniformOverride,
|
gx::render(draw.gx, pass, encodeState, renderPasses[idx].requireReadyPipelines, uniformOverride,
|
||||||
virtualScreenDraw ? draw.gx.exactScreenDepthPipeline : 0);
|
overrideTarget && invocation.target->depthFormat == wgpu::TextureFormat::Depth24PlusStencil8
|
||||||
|
? (virtualScreenDraw ? draw.gx.stereoScreenPipeline : draw.gx.stereoPipeline)
|
||||||
|
: (virtualScreenDraw ? draw.gx.exactScreenDepthPipeline : 0));
|
||||||
} break;
|
} break;
|
||||||
case ShaderType::Clear: {
|
case ShaderType::Clear: {
|
||||||
auto clearDraw = draw.clear;
|
auto clearDraw = draw.clear;
|
||||||
|
if (overrideTarget && invocation.target->depthFormat == wgpu::TextureFormat::Depth24PlusStencil8) {
|
||||||
|
clearDraw.pipeline = clearDraw.stereoPipeline;
|
||||||
|
}
|
||||||
if (multiplayer) {
|
if (multiplayer) {
|
||||||
const auto& sc = clearDraw.scissor;
|
const auto& sc = clearDraw.scissor;
|
||||||
if (clearDraw.copyClear ||
|
if (clearDraw.copyClear ||
|
||||||
|
|||||||
@@ -291,6 +291,7 @@ struct ReplayTarget {
|
|||||||
wgpu::TextureView copySourceDepthView;
|
wgpu::TextureView copySourceDepthView;
|
||||||
wgpu::Extent3D size{};
|
wgpu::Extent3D size{};
|
||||||
uint32_t msaaSamples = 1;
|
uint32_t msaaSamples = 1;
|
||||||
|
wgpu::TextureFormat depthFormat = wgpu::TextureFormat::Depth32Float;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StereoReplayEye {
|
struct StereoReplayEye {
|
||||||
|
|||||||
@@ -2206,6 +2206,10 @@ static ArrayRef<u16> offset_index_template(const CachedIndexTemplate& indexTempl
|
|||||||
|
|
||||||
struct CachedPipelineState {
|
struct CachedPipelineState {
|
||||||
gfx::PipelineRef ref = 0;
|
gfx::PipelineRef ref = 0;
|
||||||
|
const PipelineConfig* config = nullptr;
|
||||||
|
mutable gfx::PipelineRef stereoRef = 0;
|
||||||
|
mutable gfx::PipelineRef screenRef = 0;
|
||||||
|
mutable gfx::PipelineRef stereoScreenRef = 0;
|
||||||
HashType configHash = 0;
|
HashType configHash = 0;
|
||||||
// Carried here so the draw can be recorded without keeping the PipelineConfig that produced it alive; it is the only
|
// Carried here so the draw can be recorded without keeping the PipelineConfig that produced it alive; it is the only
|
||||||
// field of the config the draw itself still needs.
|
// field of the config the draw itself still needs.
|
||||||
@@ -2231,6 +2235,7 @@ static const CachedPipelineState& cached_pipeline_state(const PipelineConfig& co
|
|||||||
entry.config = config;
|
entry.config = config;
|
||||||
entry.state = {
|
entry.state = {
|
||||||
.ref = gfx::pipeline_ref(config),
|
.ref = gfx::pipeline_ref(config),
|
||||||
|
.config = &entry.config,
|
||||||
.configHash = hash,
|
.configHash = hash,
|
||||||
.dstAlpha = config.dstAlpha,
|
.dstAlpha = config.dstAlpha,
|
||||||
.shaderInfo = build_shader_info(config.shaderConfig),
|
.shaderInfo = build_shader_info(config.shaderConfig),
|
||||||
@@ -2273,37 +2278,20 @@ static const CachedPipelineState& resolve_pipeline_state(GXPrimitive prim, GXVtx
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exact Screen Depth changes shader outputs but no GX pipeline state. Resolve a
|
// Lazily cache eye-format siblings alongside the ordinary pipeline. Steady-state
|
||||||
// sibling pipeline only for orthographic draws that can actually reach the VR
|
// draws only read the refs: no extra config population/hashing on the Quest CPU.
|
||||||
// screen, and memoize it with the same state epoch as the ordinary pipeline.
|
// Shader modules are shared by the depth-format variants.
|
||||||
static gfx::PipelineRef resolve_exact_screen_depth_pipeline(GXPrimitive prim, GXVtxFmt fmt) {
|
static void resolve_replay_pipelines(const CachedPipelineState& state, bool screen) {
|
||||||
struct Memo {
|
if (state.stereoRef && (!screen || state.stereoScreenRef)) return;
|
||||||
gfx::PipelineRef ref = 0;
|
PipelineConfig config = *state.config;
|
||||||
u32 generation = 0;
|
config.stereoStencil = 1;
|
||||||
u32 sampleCount = 0;
|
if (!state.stereoRef) state.stereoRef = gfx::pipeline_ref(config);
|
||||||
GXPrimitive prim = static_cast<GXPrimitive>(0);
|
if (screen && !state.stereoScreenRef) {
|
||||||
GXVtxFmt fmt = static_cast<GXVtxFmt>(0);
|
config.shaderConfig.exactScreenDepth = 1;
|
||||||
};
|
state.stereoScreenRef = gfx::pipeline_ref(config);
|
||||||
static Memo memo{};
|
config.stereoStencil = 0;
|
||||||
|
state.screenRef = gfx::pipeline_ref(config);
|
||||||
const u32 sampleCount = gfx::get_sample_count();
|
}
|
||||||
const u32 generation = g_gxState.pipelineStateGeneration;
|
|
||||||
if (memo.ref != 0 && memo.generation == generation && memo.sampleCount == sampleCount && memo.prim == prim &&
|
|
||||||
memo.fmt == fmt)
|
|
||||||
LIKELY { return memo.ref; }
|
|
||||||
|
|
||||||
PipelineConfig config{};
|
|
||||||
populate_pipeline_config(config, prim, fmt);
|
|
||||||
config.shaderConfig.exactScreenDepth = 1;
|
|
||||||
const gfx::PipelineRef ref = gfx::pipeline_ref(config);
|
|
||||||
memo = Memo{
|
|
||||||
.ref = ref,
|
|
||||||
.generation = generation,
|
|
||||||
.sampleCount = sampleCount,
|
|
||||||
.prim = prim,
|
|
||||||
.fmt = fmt,
|
|
||||||
};
|
|
||||||
return ref;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool submit_raw_draw(GXPrimitive prim, GXVtxFmt fmt, const uint8_t* vertices, uint16_t vtxCount, uint32_t vertexBytes) {
|
bool submit_raw_draw(GXPrimitive prim, GXVtxFmt fmt, const uint8_t* vertices, uint16_t vtxCount, uint32_t vertexBytes) {
|
||||||
@@ -2508,10 +2496,9 @@ static void handle_draw_unmerged(GXPrimitive prim, GXVtxFmt fmt, u16 vtxCount, g
|
|||||||
const bool perspective = g_gxState.projType == GX_PERSPECTIVE;
|
const bool perspective = g_gxState.projType == GX_PERSPECTIVE;
|
||||||
const auto uniformRanges = build_uniform(info, vertRange.offset, ranges, drawIdentity, perspective, usedPnMtxMask);
|
const auto uniformRanges = build_uniform(info, vertRange.offset, ranges, drawIdentity, perspective, usedPnMtxMask);
|
||||||
const auto& replayLayout = uniformRanges.replayLayout;
|
const auto& replayLayout = uniformRanges.replayLayout;
|
||||||
const gfx::PipelineRef exactScreenDepthPipeline =
|
const bool stereo = aurora::stereo_frame_provider_active();
|
||||||
aurora::stereo_frame_provider_active() && !replayLayout.perspective && !replayLayout.nativeEfbEffect
|
const bool screen = !replayLayout.perspective && !replayLayout.nativeEfbEffect;
|
||||||
? resolve_exact_screen_depth_pipeline(prim, fmt)
|
if (stereo) resolve_replay_pipelines(pipelineState, screen);
|
||||||
: 0;
|
|
||||||
s_lastDrawRecordedInterpolation = interpolationIdentityActive;
|
s_lastDrawRecordedInterpolation = interpolationIdentityActive;
|
||||||
|
|
||||||
uint32_t instanceCount = 1;
|
uint32_t instanceCount = 1;
|
||||||
@@ -2524,7 +2511,9 @@ static void handle_draw_unmerged(GXPrimitive prim, GXVtxFmt fmt, u16 vtxCount, g
|
|||||||
}
|
}
|
||||||
gfx::push_draw_command(DrawData{
|
gfx::push_draw_command(DrawData{
|
||||||
.pipeline = pipeline,
|
.pipeline = pipeline,
|
||||||
.exactScreenDepthPipeline = exactScreenDepthPipeline,
|
.exactScreenDepthPipeline = stereo && screen ? pipelineState.screenRef : 0,
|
||||||
|
.stereoPipeline = stereo ? pipelineState.stereoRef : 0,
|
||||||
|
.stereoScreenPipeline = stereo && screen ? pipelineState.stereoScreenRef : 0,
|
||||||
.vertRange = vertRange,
|
.vertRange = vertRange,
|
||||||
.idxRange = idxRange,
|
.idxRange = idxRange,
|
||||||
.uniformRange = uniformRanges.current,
|
.uniformRange = uniformRanges.current,
|
||||||
|
|||||||
@@ -1609,10 +1609,18 @@ static inline wgpu::PrimitiveState to_primitive_state(GXCullMode gx_cullMode) {
|
|||||||
wgpu::RenderPipeline build_pipeline(const PipelineConfig& config, ArrayRef<wgpu::VertexBufferLayout> vtxBuffers,
|
wgpu::RenderPipeline build_pipeline(const PipelineConfig& config, ArrayRef<wgpu::VertexBufferLayout> vtxBuffers,
|
||||||
wgpu::ShaderModule shader, const char* label) noexcept {
|
wgpu::ShaderModule shader, const char* label) noexcept {
|
||||||
ZoneScoped;
|
ZoneScoped;
|
||||||
|
const bool maskCockpit = config.stereoStencil && config.shaderConfig.exactScreenDepth;
|
||||||
|
const wgpu::StencilFaceState stencil{
|
||||||
|
.compare = maskCockpit ? wgpu::CompareFunction::Equal : wgpu::CompareFunction::Always,
|
||||||
|
};
|
||||||
const wgpu::DepthStencilState depthStencil{
|
const wgpu::DepthStencilState depthStencil{
|
||||||
.format = g_graphicsConfig.depthFormat,
|
.format = config.stereoStencil ? wgpu::TextureFormat::Depth24PlusStencil8 : g_graphicsConfig.depthFormat,
|
||||||
.depthWriteEnabled = config.depthUpdate,
|
.depthWriteEnabled = config.depthUpdate,
|
||||||
.depthCompare = config.depthCompare ? to_compare_function(config.depthFunc) : wgpu::CompareFunction::Always,
|
.depthCompare = config.depthCompare ? to_compare_function(config.depthFunc) : wgpu::CompareFunction::Always,
|
||||||
|
.stencilFront = stencil,
|
||||||
|
.stencilBack = stencil,
|
||||||
|
.stencilReadMask = 1,
|
||||||
|
.stencilWriteMask = 0,
|
||||||
};
|
};
|
||||||
const auto blendState = to_blend_state(config.blendMode, config.blendFacSrc, config.blendFacDst, config.blendOp,
|
const auto blendState = to_blend_state(config.blendMode, config.blendFacSrc, config.blendFacDst, config.blendOp,
|
||||||
config.pixelFmt, config.dstAlpha);
|
config.pixelFmt, config.dstAlpha);
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ struct DrawData {
|
|||||||
// Same GX state with exact fragment-depth export enabled. Bound only when
|
// Same GX state with exact fragment-depth export enabled. Bound only when
|
||||||
// this draw is actually reprojected onto the VR virtual screen.
|
// this draw is actually reprojected onto the VR virtual screen.
|
||||||
gfx::PipelineRef exactScreenDepthPipeline;
|
gfx::PipelineRef exactScreenDepthPipeline;
|
||||||
|
// Eye depth/stencil format siblings. Shader modules are shared with mono.
|
||||||
|
gfx::PipelineRef stereoPipeline = 0;
|
||||||
|
gfx::PipelineRef stereoScreenPipeline = 0;
|
||||||
gfx::Range vertRange;
|
gfx::Range vertRange;
|
||||||
gfx::Range idxRange;
|
gfx::Range idxRange;
|
||||||
gfx::Range uniformRange;
|
gfx::Range uniformRange;
|
||||||
@@ -29,7 +32,7 @@ struct DrawData {
|
|||||||
std::optional<gfx::stereo_replay::SubviewRect> screenRect;
|
std::optional<gfx::stereo_replay::SubviewRect> screenRect;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint32_t GXPipelineConfigVersion = 20;
|
constexpr uint32_t GXPipelineConfigVersion = 21;
|
||||||
|
|
||||||
constexpr GXFogType effective_pipeline_fog_type(GXFogType fogType, GXZTexOp zTextureOp, bool zCompLocBeforeTex,
|
constexpr GXFogType effective_pipeline_fog_type(GXFogType fogType, GXZTexOp zTextureOp, bool zCompLocBeforeTex,
|
||||||
GXBlendMode blendMode, GXLogicOp logicOp) noexcept {
|
GXBlendMode blendMode, GXLogicOp logicOp) noexcept {
|
||||||
@@ -41,6 +44,7 @@ constexpr GXFogType effective_pipeline_fog_type(GXFogType fogType, GXZTexOp zTex
|
|||||||
struct PipelineConfig {
|
struct PipelineConfig {
|
||||||
uint32_t version = GXPipelineConfigVersion;
|
uint32_t version = GXPipelineConfigVersion;
|
||||||
uint32_t msaaSamples = 1;
|
uint32_t msaaSamples = 1;
|
||||||
|
uint32_t stereoStencil = 0;
|
||||||
ShaderConfig shaderConfig;
|
ShaderConfig shaderConfig;
|
||||||
GXCompare depthFunc;
|
GXCompare depthFunc;
|
||||||
GXCullMode cullMode;
|
GXCullMode cullMode;
|
||||||
@@ -61,7 +65,7 @@ inline bool valid_pipeline_config(const PipelineConfig& config) noexcept {
|
|||||||
};
|
};
|
||||||
const bool validSamples =
|
const bool validSamples =
|
||||||
config.msaaSamples == 1 || config.msaaSamples == 2 || config.msaaSamples == 4 || config.msaaSamples == 8;
|
config.msaaSamples == 1 || config.msaaSamples == 2 || config.msaaSamples == 4 || config.msaaSamples == 8;
|
||||||
return config.version == GXPipelineConfigVersion && validSamples && in_range(config.depthFunc, GX_ALWAYS) &&
|
return config.version == GXPipelineConfigVersion && validSamples && config.stereoStencil <= 1 && in_range(config.depthFunc, GX_ALWAYS) &&
|
||||||
in_range(config.cullMode, GX_CULL_ALL) && in_range(config.blendMode, GX_BM_SUBTRACT) &&
|
in_range(config.cullMode, GX_CULL_ALL) && in_range(config.blendMode, GX_BM_SUBTRACT) &&
|
||||||
in_range(config.blendFacSrc, GX_BL_INVDSTALPHA) && in_range(config.blendFacDst, GX_BL_INVDSTALPHA) &&
|
in_range(config.blendFacSrc, GX_BL_INVDSTALPHA) && in_range(config.blendFacDst, GX_BL_INVDSTALPHA) &&
|
||||||
in_range(config.blendOp, GX_LO_SET) && in_range(config.pixelFmt, GX_PF_YUV420);
|
in_range(config.blendOp, GX_LO_SET) && in_range(config.pixelFmt, GX_PF_YUV420);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ int main() {
|
|||||||
native.nativeWheel=false;
|
native.nativeWheel=false;
|
||||||
if(gfx::cockpit::geometry(native).empty()) return 1;
|
if(gfx::cockpit::geometry(native).empty()) return 1;
|
||||||
for(bool bike : {false,true}) for(bool original : {false,true}) for(uint32_t samples : {1u,4u})
|
for(bool bike : {false,true}) for(bool original : {false,true}) for(uint32_t samples : {1u,4u})
|
||||||
for(int coverage : {0,1,2}) for(bool reversed : {false,true}) for(uint32_t eyeIndex : {0u,1u}) {
|
for(bool hud : {false,true}) for(int coverage : {0,1,2}) for(bool reversed : {false,true}) for(uint32_t eyeIndex : {0u,1u}) {
|
||||||
const bool occluded=coverage==1;
|
const bool occluded=coverage==1;
|
||||||
gfx::StereoReplayFrame frame{};
|
gfx::StereoReplayFrame frame{};
|
||||||
frame.cockpit.unitsPerMeter=100;
|
frame.cockpit.unitsPerMeter=100;
|
||||||
@@ -60,10 +60,10 @@ int main() {
|
|||||||
auto output=g_device.CreateTexture(&td);
|
auto output=g_device.CreateTexture(&td);
|
||||||
td.sampleCount=samples;td.usage=wgpu::TextureUsage::RenderAttachment;
|
td.sampleCount=samples;td.usage=wgpu::TextureUsage::RenderAttachment;
|
||||||
auto color=g_device.CreateTexture(&td);
|
auto color=g_device.CreateTexture(&td);
|
||||||
td.format=wgpu::TextureFormat::Depth32Float;auto depth=g_device.CreateTexture(&td);
|
td.format=wgpu::TextureFormat::Depth24PlusStencil8;auto depth=g_device.CreateTexture(&td);
|
||||||
auto& eye=frame.eyes[eyeIndex];eye.target.colorView=samples==1?output.CreateView():color.CreateView();
|
auto& eye=frame.eyes[eyeIndex];eye.target.colorView=samples==1?output.CreateView():color.CreateView();
|
||||||
if(samples>1) eye.target.resolveView=output.CreateView();
|
if(samples>1) eye.target.resolveView=output.CreateView();
|
||||||
eye.target.depthView=depth.CreateView();eye.target.size={512,512,1};eye.target.msaaSamples=samples;
|
eye.target.depthFormat=td.format;eye.target.depthView=depth.CreateView();eye.target.size={512,512,1};eye.target.msaaSamples=samples;
|
||||||
eye.projection.m0[0]=1;eye.projection.m1[1]=1;
|
eye.projection.m0[0]=1;eye.projection.m1[1]=1;
|
||||||
eye.projection.m0[2]=eyeIndex?0.06f:-0.06f;
|
eye.projection.m0[2]=eyeIndex?0.06f:-0.06f;
|
||||||
auto view=gfx::cockpit::identity();view[7]=0.20f;
|
auto view=gfx::cockpit::identity();view[7]=0.20f;
|
||||||
@@ -72,7 +72,8 @@ int main() {
|
|||||||
const wgpu::RenderPassColorAttachment clear{.view=eye.target.colorView,.resolveTarget=eye.target.resolveView,
|
const wgpu::RenderPassColorAttachment clear{.view=eye.target.colorView,.resolveTarget=eye.target.resolveView,
|
||||||
.loadOp=wgpu::LoadOp::Clear,.storeOp=wgpu::StoreOp::Store,.clearValue={0.06,0.09,0.13,1}};
|
.loadOp=wgpu::LoadOp::Clear,.storeOp=wgpu::StoreOp::Store,.clearValue={0.06,0.09,0.13,1}};
|
||||||
const wgpu::RenderPassDepthStencilAttachment sceneDepth{.view=eye.target.depthView,
|
const wgpu::RenderPassDepthStencilAttachment sceneDepth{.view=eye.target.depthView,
|
||||||
.depthLoadOp=wgpu::LoadOp::Clear,.depthStoreOp=wgpu::StoreOp::Store,.depthClearValue=reversed?(occluded?0.8f:0.0f):(occluded?0.2f:1.0f)};
|
.depthLoadOp=wgpu::LoadOp::Clear,.depthStoreOp=wgpu::StoreOp::Store,.depthClearValue=reversed?(occluded?0.8f:0.0f):(occluded?0.2f:1.0f),
|
||||||
|
.stencilLoadOp=wgpu::LoadOp::Clear,.stencilStoreOp=wgpu::StoreOp::Store,.stencilClearValue=0};
|
||||||
const wgpu::RenderPassDescriptor pd{.colorAttachmentCount=1,.colorAttachments=&clear,.depthStencilAttachment=&sceneDepth};
|
const wgpu::RenderPassDescriptor pd{.colorAttachmentCount=1,.colorAttachments=&clear,.depthStencilAttachment=&sceneDepth};
|
||||||
auto pass=encoder.BeginRenderPass(&pd);
|
auto pass=encoder.BeginRenderPass(&pd);
|
||||||
if(coverage==2) {
|
if(coverage==2) {
|
||||||
@@ -88,7 +89,7 @@ int main() {
|
|||||||
auto shader=g_device.CreateShaderModule(&md);
|
auto shader=g_device.CreateShaderModule(&md);
|
||||||
const wgpu::ColorTargetState colorState{.format=wgpu::TextureFormat::RGBA8Unorm};
|
const wgpu::ColorTargetState colorState{.format=wgpu::TextureFormat::RGBA8Unorm};
|
||||||
const wgpu::FragmentState fragment{.module=shader,.entryPoint="fs",.targetCount=1,.targets=&colorState};
|
const wgpu::FragmentState fragment{.module=shader,.entryPoint="fs",.targetCount=1,.targets=&colorState};
|
||||||
const wgpu::DepthStencilState ds{.format=wgpu::TextureFormat::Depth32Float,.depthWriteEnabled=true,.depthCompare=wgpu::CompareFunction::Always};
|
const wgpu::DepthStencilState ds{.format=eye.target.depthFormat,.depthWriteEnabled=true,.depthCompare=wgpu::CompareFunction::Always};
|
||||||
wgpu::RenderPipelineDescriptor desc{};desc.vertex={.module=shader,.entryPoint="vs"};
|
wgpu::RenderPipelineDescriptor desc{};desc.vertex={.module=shader,.entryPoint="vs"};
|
||||||
desc.fragment=&fragment;desc.depthStencil=&ds;desc.multisample.count=samples;
|
desc.fragment=&fragment;desc.depthStencil=&ds;desc.multisample.count=samples;
|
||||||
auto wall=g_device.CreateRenderPipeline(&desc);pass.SetPipeline(wall);pass.Draw(6);
|
auto wall=g_device.CreateRenderPipeline(&desc);pass.SetPipeline(wall);pass.Draw(6);
|
||||||
@@ -96,6 +97,36 @@ int main() {
|
|||||||
if(coverage==2) gfx::cockpit::render(encoder,frame,eyeIndex,reversed?gfx::cockpit::SceneDepth{0,2,true}:gfx::cockpit::SceneDepth{-1,-2,true},&pass);
|
if(coverage==2) gfx::cockpit::render(encoder,frame,eyeIndex,reversed?gfx::cockpit::SceneDepth{0,2,true}:gfx::cockpit::SceneDepth{-1,-2,true},&pass);
|
||||||
pass.End();
|
pass.End();
|
||||||
if(coverage!=2) gfx::cockpit::render(encoder,frame,eyeIndex,reversed?gfx::cockpit::SceneDepth{0,2,true}:gfx::cockpit::SceneDepth{-1,-2,true});
|
if(coverage!=2) gfx::cockpit::render(encoder,frame,eyeIndex,reversed?gfx::cockpit::SceneDepth{0,2,true}:gfx::cockpit::SceneDepth{-1,-2,true});
|
||||||
|
if(hud) {
|
||||||
|
// An opaque black screen and coloured HUD with depth testing disabled used
|
||||||
|
// to overwrite the hands. Exercise a later pass too: the mask must survive.
|
||||||
|
const wgpu::RenderPassColorAttachment load{.view=eye.target.colorView,.resolveTarget=eye.target.resolveView,
|
||||||
|
.loadOp=wgpu::LoadOp::Load,.storeOp=wgpu::StoreOp::Store};
|
||||||
|
const wgpu::RenderPassDepthStencilAttachment loadDepth{.view=eye.target.depthView,
|
||||||
|
.depthLoadOp=wgpu::LoadOp::Load,.depthStoreOp=wgpu::StoreOp::Store,
|
||||||
|
.stencilLoadOp=wgpu::LoadOp::Load,.stencilStoreOp=wgpu::StoreOp::Store};
|
||||||
|
const wgpu::RenderPassDescriptor hudPass{.colorAttachmentCount=1,.colorAttachments=&load,.depthStencilAttachment=&loadDepth};
|
||||||
|
auto overlay=encoder.BeginRenderPass(&hudPass);
|
||||||
|
wgpu::ShaderSourceWGSL code{};
|
||||||
|
code.code=R"(
|
||||||
|
@vertex fn vs(@builtin(vertex_index) i:u32) -> @builtin(position) vec4f {
|
||||||
|
let p=array<vec2f,3>(vec2f(-1,-1),vec2f(3,-1),vec2f(-1,3));
|
||||||
|
return vec4f(p[i],0.5,1);
|
||||||
|
}
|
||||||
|
@fragment fn fs(@builtin(position) p:vec4f) -> @location(0) vec4f {
|
||||||
|
return select(vec4f(0,0,0,1),vec4f(1,0,0,1),p.x<256);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
wgpu::ShaderModuleDescriptor md{};md.nextInChain=&code;auto shader=g_device.CreateShaderModule(&md);
|
||||||
|
const wgpu::ColorTargetState colorState{.format=wgpu::TextureFormat::RGBA8Unorm};
|
||||||
|
const wgpu::FragmentState fragment{.module=shader,.entryPoint="fs",.targetCount=1,.targets=&colorState};
|
||||||
|
const wgpu::StencilFaceState mask{.compare=wgpu::CompareFunction::Equal};
|
||||||
|
const wgpu::DepthStencilState ds{.format=eye.target.depthFormat,.depthWriteEnabled=true,
|
||||||
|
.depthCompare=wgpu::CompareFunction::Always,.stencilFront=mask,.stencilBack=mask,.stencilReadMask=1,.stencilWriteMask=0};
|
||||||
|
wgpu::RenderPipelineDescriptor desc{};desc.vertex={.module=shader,.entryPoint="vs"};
|
||||||
|
desc.fragment=&fragment;desc.depthStencil=&ds;desc.multisample.count=samples;
|
||||||
|
auto screen=g_device.CreateRenderPipeline(&desc);overlay.SetPipeline(screen);overlay.Draw(3);overlay.End();
|
||||||
|
}
|
||||||
const wgpu::BufferDescriptor bd{.usage=wgpu::BufferUsage::CopyDst|wgpu::BufferUsage::MapRead,.size=512*512*4};
|
const wgpu::BufferDescriptor bd{.usage=wgpu::BufferUsage::CopyDst|wgpu::BufferUsage::MapRead,.size=512*512*4};
|
||||||
auto readback=g_device.CreateBuffer(&bd);
|
auto readback=g_device.CreateBuffer(&bd);
|
||||||
const wgpu::TexelCopyTextureInfo src{.texture=output};
|
const wgpu::TexelCopyTextureInfo src{.texture=output};
|
||||||
@@ -109,6 +140,13 @@ int main() {
|
|||||||
const auto* bytes=static_cast<const unsigned char*>(readback.GetConstMappedRange());
|
const auto* bytes=static_cast<const unsigned char*>(readback.GetConstMappedRange());
|
||||||
size_t bright=0;
|
size_t bright=0;
|
||||||
for(size_t i=0;i<512*512;++i) if(bytes[4*i]>90&&bytes[4*i+1]>90&&bytes[4*i+2]>90) ++bright;
|
for(size_t i=0;i<512*512;++i) if(bytes[4*i]>90&&bytes[4*i+1]>90&&bytes[4*i+2]>90) ++bright;
|
||||||
|
if(hud && !(bytes[0]>250 && bytes[1]==0 && bytes[2]==0)) {
|
||||||
|
std::cerr<<"HUD missing outside cockpit mask\n";++errors;
|
||||||
|
}
|
||||||
|
static size_t expectedBright[3][2][2]{};
|
||||||
|
auto& expected=expectedBright[coverage][reversed][eyeIndex];
|
||||||
|
if(!hud) expected=bright;
|
||||||
|
else if(bright+64<expected || bright>expected+64) { std::cerr<<"HUD changed visible cockpit pixels\n";++errors; }
|
||||||
if(occluded ? bright!=0 : bright<1000) { std::cerr<<"Incorrect hands/wheel occlusion\n";++errors; }
|
if(occluded ? bright!=0 : bright<1000) { std::cerr<<"Incorrect hands/wheel occlusion\n";++errors; }
|
||||||
if(coverage==2) {
|
if(coverage==2) {
|
||||||
size_t left=0,right=0;
|
size_t left=0,right=0;
|
||||||
|
|||||||
Reference in new issue
Block a user