// SPDX-License-Identifier: GPL-3.0-or-later // Ported from heurazy's mario-kart-wii-VR-port (GPL-3.0-or-later). // Renders the VR cockpit overlay on a real GPU against cleared, occluding and // partially occluding scene depth, forward and reversed, 1x and 4x MSAA. #include "../lib/gfx/cockpit.hpp" #include #include #include namespace aurora::webgpu { wgpu::Device g_device; wgpu::Queue g_queue; GraphicsConfig g_graphicsConfig{}; } std::atomic errors=0; int main() { using namespace aurora; using namespace webgpu; wgpu::InstanceDescriptor id{}; const wgpu::InstanceFeatureName timed=wgpu::InstanceFeatureName::TimedWaitAny; id.requiredFeatureCount=1;id.requiredFeatures=&timed; auto instance=wgpu::CreateInstance(&id); wgpu::Adapter adapter; wgpu::RequestAdapterOptions options{.backendType=wgpu::BackendType::D3D12}; auto future=instance.RequestAdapter(&options,wgpu::CallbackMode::WaitAnyOnly, [&](wgpu::RequestAdapterStatus status,wgpu::Adapter a,wgpu::StringView message) { if(status==wgpu::RequestAdapterStatus::Success) adapter=std::move(a); else std::cerr<1) eye.target.resolveView=output.CreateView(); 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[2]=eyeIndex?0.06f:-0.06f; auto view=gfx::cockpit::identity();view[7]=0.20f; std::memcpy(frame.cockpit.eyeFromSeat[eyeIndex],view.data(),sizeof(frame.cockpit.eyeFromSeat[eyeIndex])); auto encoder=g_device.CreateCommandEncoder(); 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}}; 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)}; const wgpu::RenderPassDescriptor pd{.colorAttachmentCount=1,.colorAttachments=&clear,.depthStencilAttachment=&sceneDepth}; auto pass=encoder.BeginRenderPass(&pd); if(coverage==2) { wgpu::ShaderSourceWGSL code{}; code.code=R"( @vertex fn vs(@builtin(vertex_index) i:u32) -> @builtin(position) vec4f { let p=array(vec2f(0,-1),vec2f(1,-1),vec2f(0,1),vec2f(0,1),vec2f(1,-1),vec2f(1,1)); return vec4f(p[i],0.5,1); } @fragment fn fs() -> @location(0) vec4f { return vec4f(0.06,0.09,0.13,1); } )"; 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::DepthStencilState ds{.format=wgpu::TextureFormat::Depth32Float,.depthWriteEnabled=true,.depthCompare=wgpu::CompareFunction::Always}; wgpu::RenderPipelineDescriptor desc{};desc.vertex={.module=shader,.entryPoint="vs"}; desc.fragment=&fragment;desc.depthStencil=&ds;desc.multisample.count=samples; auto wall=g_device.CreateRenderPipeline(&desc);pass.SetPipeline(wall);pass.Draw(6); } 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(); if(coverage!=2) gfx::cockpit::render(encoder,frame,eyeIndex,reversed?gfx::cockpit::SceneDepth{0,2,true}:gfx::cockpit::SceneDepth{-1,-2,true}); const wgpu::BufferDescriptor bd{.usage=wgpu::BufferUsage::CopyDst|wgpu::BufferUsage::MapRead,.size=512*512*4}; auto readback=g_device.CreateBuffer(&bd); const wgpu::TexelCopyTextureInfo src{.texture=output}; const wgpu::TexelCopyBufferInfo dst{.layout={.bytesPerRow=2048,.rowsPerImage=512},.buffer=readback}; const wgpu::Extent3D extent{512,512,1};encoder.CopyTextureToBuffer(&src,&dst,&extent); auto commands=encoder.Finish();g_device.GetQueue().Submit(1,&commands); bool mapped=false; future=readback.MapAsync(wgpu::MapMode::Read,0,512*512*4,wgpu::CallbackMode::WaitAnyOnly, [&](wgpu::MapAsyncStatus status,wgpu::StringView) { mapped=status==wgpu::MapAsyncStatus::Success; }); if(instance.WaitAny(future,5000000000)!=wgpu::WaitStatus::Success||!mapped) return 1; const auto* bytes=static_cast(readback.GetConstMappedRange()); 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; if(occluded ? bright!=0 : bright<1000) { std::cerr<<"Incorrect hands/wheel occlusion\n";++errors; } if(coverage==2) { size_t left=0,right=0; for(size_t y=0;y<512;++y) for(size_t x=0;x<512;++x) { const auto i=y*512+x; if(bytes[4*i]>90&&bytes[4*i+1]>90&&bytes[4*i+2]>90) (x<256?left:right)++; } if(left<500||right>8) { std::cerr<<"Partial wall occlusion failed for eye "<(bytes+i*4),3); } readback.Unmap(); std::cout<<(bike?"Bike ":"Kart ")<<(original?"native hands: ":"VR controls: ")<