mirror of
https://github.com/simplysanatori/KartFrame.git
synced 2026-10-23 11:01:51 +02:00
121 lines
6.9 KiB
C++
121 lines
6.9 KiB
C++
#include <webgpu/webgpu_cpp.h>
|
|
#include "../lib/webgpu/orientation_probe.hpp"
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <string>
|
|
|
|
// Exercise the production copy shader, including its specialization constants,
|
|
// using asymmetric pixels and actual D3D12 readback, without a headset.
|
|
int main(int argc, char** argv) {
|
|
if (argc != 2) return 2;
|
|
std::ifstream file(argv[1]);
|
|
const std::string cpp{std::istreambuf_iterator<char>(file),{}};
|
|
const auto begin=cpp.find("sourceDescriptor.code = R\"\"\"(");
|
|
if(begin==std::string::npos) return 2;
|
|
const auto start=cpp.find('(',begin)+1, end=cpp.find(")\"\"\";",start);
|
|
if(end==std::string::npos) return 2;
|
|
const auto code=cpp.substr(start,end-start);
|
|
static std::atomic<int> errors{0};
|
|
const auto timed=wgpu::InstanceFeatureName::TimedWaitAny;
|
|
wgpu::InstanceDescriptor id{};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) {
|
|
if(status==wgpu::RequestAdapterStatus::Success) adapter=std::move(a);
|
|
});
|
|
if(instance.WaitAny(future,5000000000)!=wgpu::WaitStatus::Success||!adapter) return 2;
|
|
wgpu::Device device;
|
|
wgpu::DeviceDescriptor dd{};
|
|
dd.SetUncapturedErrorCallback([](const wgpu::Device&,wgpu::ErrorType,wgpu::StringView text) {
|
|
++errors;std::cerr<<std::string_view(text)<<'\n';
|
|
});
|
|
future=adapter.RequestDevice(&dd,wgpu::CallbackMode::WaitAnyOnly,
|
|
[&](wgpu::RequestDeviceStatus status,wgpu::Device d,wgpu::StringView) {
|
|
if(status==wgpu::RequestDeviceStatus::Success) device=std::move(d);
|
|
});
|
|
if(instance.WaitAny(future,5000000000)!=wgpu::WaitStatus::Success||!device) return 2;
|
|
wgpu::ShaderSourceWGSL wgsl{};wgsl.code=code.c_str();
|
|
wgpu::ShaderModuleDescriptor md{};md.nextInChain=&wgsl;
|
|
auto shader=device.CreateShaderModule(&md);
|
|
const wgpu::ColorTargetState color{.format=wgpu::TextureFormat::RGBA8Unorm};
|
|
const wgpu::FragmentState fragment{.module=shader,.entryPoint="fs_main",.targetCount=1,.targets=&color};
|
|
const wgpu::TextureDescriptor inputDesc{.usage=wgpu::TextureUsage::TextureBinding|wgpu::TextureUsage::CopyDst,
|
|
.size={4,4,1},.format=wgpu::TextureFormat::RGBA8Unorm};
|
|
auto input=device.CreateTexture(&inputDesc);
|
|
std::array<unsigned char,64> pixels{};
|
|
for(int i=0;i<16;++i) {pixels[i*4]=static_cast<unsigned char>(i*13);pixels[i*4+3]=255;}
|
|
const wgpu::TexelCopyTextureInfo inputCopy{.texture=input};
|
|
const wgpu::TexelCopyBufferLayout layout{.bytesPerRow=16,.rowsPerImage=4};
|
|
const wgpu::Extent3D inputSize{4,4,1};
|
|
device.GetQueue().WriteTexture(&inputCopy,pixels.data(),pixels.size(),&layout,&inputSize);
|
|
auto sampler=device.CreateSampler();
|
|
std::array<wgpu::RenderPipeline,4> copies;
|
|
int checked=0;
|
|
for(int mode=0;mode<4;++mode) for(uint32_t scale : {1u,2u}) for(int eye=0;eye<2;++eye) {
|
|
const std::array constants{wgpu::ConstantEntry{.key="flip_x",.value=mode>=2?1.0:0.0},
|
|
wgpu::ConstantEntry{.key="flip_y",.value=mode==1||mode==2?1.0:0.0}};
|
|
wgpu::RenderPipelineDescriptor pd{};
|
|
pd.vertex={.module=shader,.entryPoint="vs_main",.constantCount=constants.size(),.constants=constants.data()};
|
|
pd.fragment=&fragment;
|
|
auto pipeline=device.CreateRenderPipeline(&pd);
|
|
copies[mode]=pipeline;
|
|
const std::array entries{wgpu::BindGroupEntry{.binding=0,.sampler=sampler},
|
|
wgpu::BindGroupEntry{.binding=1,.textureView=input.CreateView()}};
|
|
const wgpu::BindGroupDescriptor bgd{.layout=pipeline.GetBindGroupLayout(0),.entryCount=entries.size(),.entries=entries.data()};
|
|
auto binding=device.CreateBindGroup(&bgd);
|
|
const uint32_t size=4*scale;
|
|
const wgpu::TextureDescriptor outDesc{.usage=wgpu::TextureUsage::RenderAttachment|wgpu::TextureUsage::CopySrc,
|
|
.size={size,size,1},.format=wgpu::TextureFormat::RGBA8Unorm};
|
|
auto output=device.CreateTexture(&outDesc);
|
|
auto encoder=device.CreateCommandEncoder();
|
|
const wgpu::RenderPassColorAttachment attachment{.view=output.CreateView(),.loadOp=wgpu::LoadOp::Clear,.storeOp=wgpu::StoreOp::Store};
|
|
const wgpu::RenderPassDescriptor passDesc{.colorAttachmentCount=1,.colorAttachments=&attachment};
|
|
auto pass=encoder.BeginRenderPass(&passDesc);
|
|
pass.SetPipeline(pipeline);pass.SetBindGroup(0,binding);pass.Draw(3);pass.End();
|
|
const wgpu::BufferDescriptor bd{.usage=wgpu::BufferUsage::MapRead|wgpu::BufferUsage::CopyDst,.size=256*size};
|
|
auto buffer=device.CreateBuffer(&bd);
|
|
const wgpu::TexelCopyTextureInfo src{.texture=output};
|
|
const wgpu::TexelCopyBufferInfo dst{.layout={.bytesPerRow=256,.rowsPerImage=size},.buffer=buffer};
|
|
const wgpu::Extent3D extent{size,size,1};encoder.CopyTextureToBuffer(&src,&dst,&extent);
|
|
auto command=encoder.Finish();device.GetQueue().Submit(1,&command);
|
|
bool mapped=false;
|
|
future=buffer.MapAsync(wgpu::MapMode::Read,0,256*size,wgpu::CallbackMode::WaitAnyOnly,
|
|
[&](wgpu::MapAsyncStatus status,wgpu::StringView) {mapped=status==wgpu::MapAsyncStatus::Success;});
|
|
if(instance.WaitAny(future,5000000000)!=wgpu::WaitStatus::Success||!mapped) return 2;
|
|
const auto* result=static_cast<const unsigned char*>(buffer.GetConstMappedRange());
|
|
for(uint32_t y=0;y<size;++y) for(uint32_t x=0;x<size;++x) {
|
|
const auto sx=mode>=2?3-x/scale:x/scale;
|
|
const auto sy=mode==1||mode==2?3-y/scale:y/scale;
|
|
if(result[y*256+x*4]!=pixels[(sy*4+sx)*4]) ++errors;
|
|
}
|
|
buffer.Unmap();++checked;
|
|
}
|
|
for(auto format : {wgpu::TextureFormat::RGBA8Unorm,wgpu::TextureFormat::BGRA8Unorm}) {
|
|
const wgpu::ColorTargetState probeColor{.format=format};
|
|
const wgpu::FragmentState probeFragment{.module=shader,.entryPoint="fs_main",.targetCount=1,.targets=&probeColor};
|
|
for(int mode=0;mode<4;++mode) {
|
|
const std::array constants{wgpu::ConstantEntry{.key="flip_x",.value=mode>=2?1.0:0.0},
|
|
wgpu::ConstantEntry{.key="flip_y",.value=mode==1||mode==2?1.0:0.0}};
|
|
wgpu::RenderPipelineDescriptor pd{};
|
|
pd.vertex={.module=shader,.entryPoint="vs_main",.constantCount=constants.size(),.constants=constants.data()};
|
|
pd.fragment=&probeFragment;copies[mode]=device.CreateRenderPipeline(&pd);
|
|
}
|
|
for(int injection=0;injection<4;++injection) {
|
|
auto candidates=copies;
|
|
// Simulate a driver/backend changing the default output transform. The
|
|
// detector must choose the measured upright pipeline without a preference.
|
|
std::swap(candidates[0],candidates[injection]);
|
|
const auto result=aurora::webgpu::probe_output_orientation(instance,device,format,candidates);
|
|
if(!result.valid || result.needsCopy || result.copyMode!=injection) ++errors;
|
|
++checked;
|
|
}
|
|
}
|
|
std::cout<<checked<<" D3D12 orientation and automatic detection cases, "<<errors<<" errors\n";
|
|
device.Destroy();return errors?1:0;
|
|
}
|